An immutable, portable, serializable, restorable, and extensible container of time-series data. You can add/remove values and new instance will be created. It can be easily integrated with Redux, sp2, and Phenyl.
$ npm install --save versioned-value-mapimport { VersionedValueMap } from "versioned-value-map";const map = new VersionedValueMap();
const newMap = map.$add("propName01", "foobar");map is unchanged. newMap forms the following structure.
{
items: {
propName01: {
name: 'propName01',
records: [
{ value: 'foobar', at: '2018-03-02T18:56:00.222Z' },
],
},
bar: {
...
}
}
}You can see that the timestamp is automatically added into the records.
The 3rd argument is reserved for timestamp.
const newMap = map.$add("propName01", "foobar", "2018-03-02T18:56:00.222Z");Call map.get(name) to get the current value of the Value Map.
const map = new VersionedValueMap().$add("propName01", "foobar");
const currentValue = map.get("propName01");
assert(currentValue === "foobar");Trying to get non-registered value will return null.
const currentValue = map.get("abc");
assert(currentValue == null);const item = map.getItem("propName01");item is an instance of VersionedValue that forms the following structure:
{
name: 'propName01',
records: [
{ value: 'foobar', at: '2018-03-02T18:56:00.222Z' }
]
}All these properties are public and it can be accessed like the following:
const createdAt = map.getItem("propName01").records[0].at;Create a new map with newest value removed from it:
const newMap = map.$removeNewest("propName01");You can see that map is unchanged.
You can use map.$remove(name, at) to remove a specific value at specific timestamp.
const newMap = map.$remove("propName01", "2018-03-02T18:56:00.222Z");VersionedValueMap is Restorable.
That means it can be re-created by passing its JSON object to the class constructor.
In the following case, map is deeply equal to newMap.
const plainMap = JSON.parse(JSON.stringify(map));
const newMap = new VersionedValueMap(plainMap);The plain map's structure is as follows.
{
items: {
foo: {
name: 'foo',
records: [
{ value: 1, at: '2018-03-02T18:56:00.222Z' },
{ value: 7, at: '2018-03-04T03:11:23.524Z' },
],
},
bar: {
...
}
}
}const map = new VersionedValueMap();
const operation = map.add("propName01", "foobar");Unlike $add() which directly creates a new map, add() creates an UpdateOperation instead.
operation here contains the operation to update map as data.
{
$set: {
'items.propName01': { name: 'propName01', records: [{ value: 'foobar', at: '2018-03-02T18:56:00.222Z' }] }
}
}This format is almost the same as MongoDB's Update Operators. See sp2 Documentation for more detailed information.
UpdateOperation can be parsed by a simple library called sp2.
Pass the operation generated above to update() to create a new object.
import { update } from "@sp2/updater";
const newPlainMap = update(oldMap, operation); // NewMap = OldMap + UpdateOperation
const newMap = new VersionedValueMap(newPlainMap);Since update() returns a plain object, you will need to call constructor afterwards to create a new VersionValueMap.
Alternatively, you can call updateAndRestore() to automatically create a new VersionValueMap.
import { updateAndRestore } from "@sp2/updater";
const newMap = updateAndRestore(oldMap, operation);Writing these code in Reducer function will let you handle the state of VersionedValueMap with Redux.
First, let's define the reducer.
import { updateProp } from '@sp2/udpater'
function reducer(state, action) {
if (!state) {
return { map: {} } // expect plain VersionedValueMap
}
if (action.type === 'update-map') {
const updateOperation = action.payload
// This immutably updates the update operation to "map"
return updateProp(state, 'map', updateOperation)
}
...
}updateProp() is like update() but it updates not to the state but to state.map.
Action can be dispatched like this:
const state = store.getState();
const map = new VersionedValueMap(state.map);
const updateOperation = map.add("propName01", "foobar");
const action = {
type: "update-map",
payload: updateOperation
};
dispatch(action);Make sure that state contains a plain map object and every time reducer is called the map is constructed by new VersionedValueMap(state.map).
We've benchmarked the performance and found that a map with 5000 items containing 10 datapoints will be constructed within 1msec (in Node.js v8).
That means we can ignore the construction cost in modern JS environments.
You can write more robust codes with TypeScript.
Put type map in initializing instances as below:
import { VersionedValueMap } from "versioned-value-map/jsnext";
const map: VersionedValueMap<{
foo: string,
bar: number
}> = new VersionedValueMap();Then, TypeScript can get its types.
const str = map.get("foo");
if (str != null) {
// here, str is regarded as string
}
const num = map.get("bar");
if (num != null) {
// here, num is regarded as number
}TBD
Apache License 2.0