1.0.7 • Published 5 years ago

direct-object v1.0.7

Weekly downloads
9
License
MIT
Repository
-
Last release
5 years ago

Direct Object - A Lightweight Extension to native js plain object

Today's js development is heavy based on framework like React, Vue. Most framework works much better for pure native object rather than class. The design of js object makes it a nature map, with levels of nesting, it even makes a tree, but lack of general purpose methods for those usages. That's where the Objectex comes in, I implement several methods for frequently used general purpose object functions like Map functions, Tree functions to help accelerate the actual development.

Install

The recommended way to install and maintain direct-object in your project is through the Node.js Pacakge Manager (NPM), simply type the npm command in your project folder:

npm install direct-object

Usage

Direct Object support the reactivity of Vue. To ensure the library work properly with Vue, you should use following code to set environment:

import Vue from 'vue'
import { RuntimeEnvironment } from 'direct-object'

RuntimeEnvironment.AttachToVue(Vue)

Introduction

In Direct Object library, there are several basic types.

DirectMapObject\

DirectMapObject\ is a plain object with string key and value of type T, thus, any plain object can be treat as DirectMapObject\. The DirectMap utility provides several functions treat a DirectMapObject as a string-index array. Like Every, Filter, Find, Foreach, Slice, Some, Splice, those funtion has similiar behavior to the same-name function for Array. The map function of Array corresponding to ValueMap, and the DoubleMap function provide a way to map both key and value to create a new DirectMapObject. FindKey is the corresponding version of findIndex.

let map = { a: 1, b: 2, c: 3 }
DirectMap.Foreach(map, (value, key) => console.log(key, value))
/*
 Output:
 a 1
 b 2
 c 3
 */

let map2 = DirectMap.ValueMap(map, (value, key) => value + 1) // map2 = { a: 2, b: 3, c: 4 }

map2 = DirectMap.DoubleMap(map, (value, key) => ({ key: `${key}${key}`, value: 2 * value })) // map2 = { aa: 2, bb: 4, cc: 6 }

DirectMap.Every(map, (value, key) => value > 0) // true
DirectMap.Every(map, (value, key) => value > 2) // false
DirectMap.Some(map, (value, key) => value < 2) // true
DirectMap.Some(map, (value, key) => value > 9) // false

map2 = DirectMap.Slice(map, ['a', 'b']) // map2 = { a: 1, b: 2 }, map = { a: 1, b: 2, c: 3 }
map2 = DirectMap.Splice(map, ['a']) // map2 = { a: 1 }, map = { b: 2, c: 3 }
map2 = DirectMap.Exclude({ a: 1, b: 2, c: 3 }, ['b']) // map2 = { a: 1, c: 3 }

let value = DirectMap.Find(map, (value, key) => value > 2) // value = 3
let key = DirectMap.Find(map, (value, key) => value > 2) // key = 'c'

Also, the utitliy provides a From function to create a DirectMapObject from array, a Pack function to create object from key & value, and a ToMap function to convert the object to an ES Map Object.

let map = DirectMap.From([1,2,3], (value, index) => ({ key: `k${index}`, value })) // map = { k0: 1, k1: 2, k2: 3 }
let pack = DirectMap.Pack('a', 1) // pack = { a: 1 }

DirectTreeObject\

DirectTreeObject\ is a plain object which can be treated as a tree structure with leaf of type T. For example, { m1: { b1: 1, b2: 2 }, b3: 3 }, can be treated as:             /     \          m1     b3         /    \       |       b1    b2   3        |        |       1       2 The DirectTree utility provides a series of functions for data manipulation in this tree-like structure, which is very useful in data-drive framework like Vue, since the reactivity is strongly related to plain object. To access a branch level data in tree structure, we use path like 'key/subKey/grandKey' to replace a cascade [key][subkey][grandKey] accessor form. Get, Set, Delete provides the basic operations on tree's branch or leaf. GetBranch may be a little confusing, for any path, GetBranch return it's parent branch, and the key of the path in this branch.

let tree = { a0: { b1: { c2: 2 }, b3: 3 }, a4: { b5: 5 }, a6: { b7: 7, b8: { c9: 9, c10: 10 } } }
let value = DirectTree.Get(tree, 'a0/b1/c2') // value = 2
value = DirectTree.Get(tree, 'a6/b8') // value = { c9: 9, c10: 10 }

DirectTree.Set(tree, 'a4/b5', { c5: 12 }) // tree = { a0: { b1: { c2: 2 }, b3: 3 }, a4: { b5: { c5: 12 } }, a6: { b7: 7, b8: { c9: 9, c10: 10 } } }
DirectTree.Set(tree, 'a6', 6) // tree = { a0: { b1: { c2: 2 }, b3: 3 }, a4: { b5: { c5: 12 } }, a6: 6 }

DirectTree.Delete(tree, 'a4/b5') // tree = { a0: { b1: { c2: 2 }, b3: 3 }, a4: { }, a6: 6 }

let branch = DirectTree.GetBranch(tree, 'a0/b1/c2') // branch = { parent: b1: { c2: 2 }, key: 'c2' }

The utility also provides some advanced functions: MergeAt merges another tree into given tree's branch; MergeInternal merge another tree into the first tree, if a path exists in both trees and represents a data leaf at least in one tree, the value of second tree is reserved; MergeExternal do the same thing as MergeInternal only that a new tree is created for merged result and the original trees are unchanged. OperateLeafInternal\ apply a function (In...) => In on trees with same structure and same type and save the result to the given outTree. OperateLeafExternal\<In, Out> can accepted a diffrent output type parameter and return the computed result. Swap swap the branch or leaf at same path of two trees.

let tree1 = { x: 0, y: 0, z: 0 }
DirectTree.MergeAt(tree1, 'z', { i: 0, j: 0 }) // tree1 = { x: 0, y: 0, z: { i: 0, j: 0 } }

tree1 = { x: 0, y: { i: 0, j: 0 } }
DirectTree.MergeAt(tree1, 'y', 0) // tree1 = { x: 0, y: 0 }

let tree2 = { x: 0, z: { i: 0 }, m: 0 }
let tree3 = { y: 0, z: { j: 0 }, m: 1 }
DirectTree.MergeInternal(tree2, tree3) // tree2 = { x: 0, z: { i: 0, j: 0 }, m: 1, y: 0 }

let tree4 = DirectTree.MergeExternal({ x: 0, z: { i: 0 } }, { y: 0 }) // tree4 = { x: 0, z: { i: 0 }, y: 0 }

let tree5 = { x: -1, y: -2, z: { i: -3, j: -4 } }
let tree6 = { x: 1, y: 2, z: { i: 3, j: 4 } }
DirectTree.OperateLeafInternal<number>(tree5, (a, b) => a + b, tree6) // tree5 = { x: 0, y: 0, z: { i: 0, j: 0 } }

let tree7 = DirectTree.OperateLeafExternal<number, string>({ x: 0, y: 1 }, (a, b) => `${a}.${b}`, { x: 3, y: 4 }) // tree7 = { x: '0.3', y: '1.4' }
let tree8 = DirectTree.OperateLeafExternal<any, string>({ x: 'a', y: 'b' }, (a, b) => a + b, { x: 1, y: 2 }) // tree8 = { x: 'a1', y: 'b2' }

let tree9 = { x: 0, y: { i: 0, j: 0 } }
let tree10 = { x: 1, y: 1 }
DirectTree.Swap(tree9, tree10, 'y') // tree9 = { x: 0, y: 1 }, tree10 = { x: 1, y: { i: 0, j: 0 } }

Finally, the DirectTree utility provides a Flat a tree object as DirectMapObject, by make each leaf as a value and it's path as key (flatten key), and Deflat is the reverse action of Flat. And the IsFlat determines if a tree is flat (no branch).

let tree1 = DirectTree.Flat({ a: { b: { c: 1 } }, d: 1 }) // tree1 = { 'a/b/c': 1, d: 1 }

let tree2 = DirectTree.Deflat({ 'a.b.c': 1, d: 1 }, '.') // tree2 = { a: { b: { c: 1 } }, d: 1 }

let isFlat1 = DirectTree.IsFlat({ 'a/b/c': 1, d: 1 }) // true
let isFlat2 = DirectTree.IsFlat({ a: 1, d: 1 }) // true
let isFlat3 = DirectTree.IsFlat({ 'a/b/c': 1, d: { e: 1 } }) // false, since branch { e: 1 } detected

Generic

Generic utility provides some Is* function for type predicate, such as number, array, plain object. and a IsEmpty function for empty predicate: null & empty array & empty object & empty string. A Clone for object value clone (deep or nondeep). a Equals for deep value comparison. A Assign function to assign all leaf of an object to be a specific value and a Null to assign all leaf as null. The most special function is the Common function, it can find the common parts of an array of objects, a eps parameter is used for float/double precision.

let is1 = Generic.IsString('string') // true
let is2 = Generic.IsObject(1) // false

let is3 = Generic.IsEmpty(null) && Generic.IsEmpty({}) && Generic.IsEmpty([]) && Generic.IsEmpty('') // true

let origin = { x: 0, y: { z: { m: 0, n: 0 }, p: { i: 0, j: 0 } } }
let copy = Generic.Clone(origin, { deep: false })
copy.y.z.m = 1 // origin = { x: 0, y: { z: { m: 1, n: 0 }, p: { i: 0, j: 0 } } }
let copy2 = Generic.Clone(origin) // deep is true in default
copy2.y.z.n = 1 // origin = { x: 0, y: { z: { m: 1, n: 0 }, p: { i: 0, j: 0 } } }
let copy3 = Generic.Clone(origin, { deep: true, deepExcludes: ['p'] }) // deepExlcudes list the key that should not be deep cloned
copy3.y.z.n = 1
copy3.y.p.i = 1 // origin = { x: 0, y: { z: { m: 1, n: 0 }, p: { i: 1, j: 0 } } }

let e1 = Generic.Equals({ x: 0 }, { x: 0 }) // true
let e2 = Generic.Equals([1,2,3], [1,2,3.001]) // false
let e2 = Generic.Equals([1,2,3], [1,2,3.001], 0.1) // true, eps = 0.1 is provided.

let t = { a: 12, b: 3 }
let t2 = Generic.Assign(t, 0) // t2 = { a: 0, b: 0 }, t = { a: 12, b: 3 }
let t3 = Generic.Null({ x: 1, y: { z: 1, w: 1 } }) // t3 = { x: null, y: { z: null, w: null } }

let c1 = Generic.Common([1, 1, 1, 1.0001]) // null
let c1 = Generic.Common([1, 1, 1, 1.0001], 0.001) // 1, eps = 0.001 is provided.
let c2 = Generic.Common([{ x: 1, y: 1 }, { x: 1 }]) // { x: 1 }, y exists only in first object, so the result doesn't have a 'y' property.
let c3 = Generic.Common([{ x: 1, y: 1 }, { x: 1, y: 2 }]) // { x: 1, y: null }, y exists on both objects, but with different value, the common result has a 'y' property with null value.

DirectMath

DirectMath utility provides a object extension for Math, functions in DirectMath support computations between objects, or between objects and numbers.

let point = DirectMath.Add({x: 1, y: 1}, { x: 2, y: 2}, 3) // { x: 6, y: 6 }

DirectFitting

DirectFitting provide a customable fitter on plain objects. You can set custom fitter for different property.

let fitting = new DirectFitting()
let f1 = fitting.Fit(0.5, { x: 0, y: 0 }, { x: 1, y: 1 }) // {x : 0.5, y: 0.5 }
let f2 = fitting.Fit(0.3, 'a', 'c') // a, string is non-fittable in normal case
let f3 = fitting.Fit(0.7, 'a', 'c') // c

// you can use custom fitter by RegisterCustomFitter
fitting.RegisterCustomFitter('custom1', (p, start, end) => {
  let cs = start.charCodeAt(0)
  let ce = end.charCodeAt(0)
  return String.fromCharCode(Math.round(ce - cs) * p + cs)
})
// Set property that use the custom filter
fitting.RegisterCustomFittables('char', 'custom1')

let f4 = fitting.Fit(0.5, { n: 'a', char: 'a' }, { n: 'c', char: 'c' }) // { n: 'a', char: 'b' }

License

Distributed under the MIT license. See LICENSE for detail.