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-map
import { 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