immutable-hash

An immutable hash structure with delta journaling

immutable-hash

An immutable hash structure with delta journaling

var assert = require("assert")
var ImmutableHash = require("immutable-hash")
 
var hash = ImmutableHash()
 
var hash2 = hash.patch({ foo: "bar" })
assert.equal(hash2.get("foo"), "bar")
 
var hash3 = hash2.patch("foo", "baz")
assert.equal(hash3.get("foo"), "baz")
 
var hash4 = hash3.patch("foo", null)
assert.equal(hash4.get("foo"), )
assert.equal(hash4.has("foo"), false)
 
var hash5 = hash4.patch({ bar: { baz: true } })
assert.deepEqual(hash5.get("bar").toJSON(), { baz: true })
assert.equal(hash5.get("bar.baz"), true)
 
var hash6 = hash5.patch({ bar: { fuux: false } })
assert.equal(hash6.get("bar.fuux"), false)
assert.equal(hash6.get("bar.baz"), true)
 
var hash7 = hash6.patch("bar.baz", "hello world")
assert.equal(hash7.get("bar.baz"), "hello world")
 
 
var hash8 = hash7.map("bar", function (x) {
    return String(x)
})
assert.equal(hash8.get("bar.fuux"), "false")
 
var hash9 = hash8.patch({ baz: { one: "hello", two: "world" } })
assert.deepEqual(hash9.get("baz").toJSON(), { one: "hello", two: "world" })
 
var hash10 = hash9.map(function (x) {
    return x.patch("prop", { live: 42 })
})
assert.deepEqual(hash10.toJSON(), {
    bar: {
        prop: {
            live: 42
        },
        fuux: "false",
        baz: "hello world"
    },
    baz: {
        one: "hello",
        two: "world",
        prop: {
            live: 42
        }
    }
})
  • diff()
  • Make map() and filter() call patch() once.
  • Improve performance & benchmarks
  • Make integration() test at least as fast as diffpatcher
$ npm run bench
 
> immutable-hash@0.1.2 bench /home/raynos/Documents/immutable-hash
> node ./benchmarks
 
# ImmutableHash patch()
# --- patch(foo.bar, baz) x 765,697 ops/sec @ 1306 milliseconds elapsed
# --- patch([foo, bar], baz) x 738,007 ops/sec @ 1355 milliseconds elapsed
# --- patch({ foo: { bar: baz } }) x 569,476 ops/sec @ 1756 milliseconds elapsed
# ImmutableHash patch(key, value)
# --- patch(foo, bar) at 0 x 862,069 ops/sec @ 116 milliseconds elapsed
# --- patch(foo, bar) at 10 x 152,905 ops/sec @ 654 milliseconds elapsed
# --- patch(foo, bar) at 100 x 82,372 ops/sec @ 1214 milliseconds elapsed
# --- patch(foo, bar) at 1000 x 65,274 ops/sec @ 1532 milliseconds elapsed
# --- nested patch(foo, bar) at 1000 x 47,755 ops/sec @ 2094 milliseconds elapsed
# integration(0)
# --- ImmutableHash x 28,409 ops/sec @ 352 milliseconds elapsed
# --- diffpatcher x 27,701 ops/sec @ 361 milliseconds elapsed
# integration(10)
# --- ImmutableHash x 9,225 ops/sec @ 542 milliseconds elapsed
# --- diffpatcher x 7,062 ops/sec @ 708 milliseconds elapsed
# integration(100)
# --- ImmutableHash x 1,259 ops/sec @ 794 milliseconds elapsed
# --- diffpatcher x 1,221 ops/sec @ 819 milliseconds elapsed
# integration(1000)
# --- ImmutableHash x 81 ops/sec @ 2466 milliseconds elapsed
# --- diffpatcher x 111 ops/sec @ 1804 milliseconds elapsed

ImmutableHash is slower at larger size hashes

ImmutableHash :: initial:Object<String, Any> -> ImHash

Creates an ImmutableHash with optionally initial state.

var hash = ImHash().patch({ foo: "1", bar: { baz: "2" } })
var res = hash.toJSON() // { foo: "1", bar: { baz: "2" } } 
var hash2 = ImHash({ foo: "1", bar: { baz: "2" } })
var res2 = hash2.toJSON() // { foo: "1", bar: { baz: "2" } } 
patch :: ImHash -> parts:[String] -> value:Any -> ImHash
patch :: ImHash -> path:String -> value:Any -> ImHash
patch :: ImHash -> delta:Object<String, Any> -> ImHash

Returns a new ImHash with the patch applied to it

var hash = ImHash()
var hash2 = hash.patch(["bar", "baz"], "2")
var res2 = hash2.toJSON() // { bar: { baz: "2" } } 
var hash3 = hash.patch("bar.baz", "2")
var res3 = hash3.toJSON() // { bar: { baz: "2" } } 
var hash4 = hash.patch({ foo: "1", baz: { baz: "2" } })
var res4 = hash4.toJSON() // { foo: "1", baz: { baz: "2" } } 
toJSON :: ImHash -> Object

Returns a normal JavaScript object representation of the ImHash

var hash = ImHash({ foo: "1", bar: { baz: "2" } })
var res = hash.toJSON() // { foo: "1", bar: { baz: "2" } } 
get :: ImHash -> String -> Any

Returns the value associated with the key. Can either be a key or a nested query key.

var hash = ImHash({ foo: "1", bar: { baz: "2" } })
var foo = hash.get("foo") // "1" 
var bar = hash.get("bar") // <ImHash> 
var baz1 = bar.get("baz") // "2" 
var baz2 = hash.get("bar.baz") // "2" 
has :: ImHash -> String -> Boolean

Returns a boolean indicating whether the key is found. Can either be a key or a nested query key.

var hash = ImHash({ foo: "1", bar: { baz: "2" } })
var foo1 = hash.has("foo") // true 
var foo2 = hash.has("foo.not-exist") // false 
var bar = hash.has("bar") // true 
var baz1 = hash.has("baz") // false 
var baz2 = bar.has("baz") // true 
var baz3 = hash.has("bar.baz") // true 
var baz4 = hash.has("bar.non-exist") // false 
map :: ImHash<String, A> -> lambda:(A -> B) -> ImHash<String, B>
map :: ImHash -> path:String -> (A -> B) -> ImHash

Takes a path, get's the ImHash hash at that location. Then patches it by calling a lambda function on each value in it and replacing that key with the returned value.

Returns an ImHash with hash replaced with the patched hash.

var state = ImmutableHash({
    todos: {
        1: { title: "do work", completed: false },
        2: { title: "do more work", completed: true }
    }
})
var toggled = Math.random() < 0.5 ? true : false
var newState = state.map("todos", function (todo) {
    return todo.patch("completed", toggled)
})
filter :: ImHash<String, A> -> lambda:(A -> Boolean) -> ImHash<String, A>
filter :: ImHash -> path:String -> (A -> Boolean) -> ImHash

Takes a path, get's the ImHash hash at that location. It then patches it by calling a lambda function on each value in it and if the lambda returns false that value get's removed.

Returns an ImHash with hash replaced with the patched hash.

var hash = ImmutableHash({
    todos: {
        "1": { title: "do work", completed: false },
        "2": { title: "do more work", completed: false },
        "3": { title: "implement immutable", completed: true }
    }
})
var newState = state.filter("todos", function (todo) {
    return !todo.get("completed")
})
diff:: this:ImHash -> other:ImHash -> Object

returns an object representation of the values that are different between the two hashes.

This is optimized for the case where this is created by patching other. Which means you can do an very efficient curr.diff(prev) call.

var hash = ImmutableHash({ foo: "bar" })
var hash2 = hash.patch({ bar: "baz" })
var diff = hash2.diff(hash) // { bar: "baz" } 

npm install immutable-hash

  • Raynos