literal v0.0.1
Literal
JavaScript utility library for manipulating literals.
This library helps running complex and long operations on literals using a more concise syntax.
Instead of writing:
if (school
&& school.student
&& school.student.name
&& school.student.name.first) {
// welcome first
}
You can now write:
if (literal(school).check("student.name.first")) {
// welcome first
}
This removes a lot of clutter code.
Let's consider another example.
Instead of writing:
if (literal(school).check("student.name.first")
&& literal(school).check("student.name.last")) {
// welcome
}
You can use the built-in shorthand:
if (literal(school).check(["student.name.first", "student.name.last"])) {
// welcome first + last
}
You could get creative and optimize:
if (literal(school).check("student.name")
&& literal(school.student.name).check(["first", "last"])) {
// welcome first + last
}
Eventually it will maintain a cache of traversed paths, but for the time-being you can use the above to check a common path, and then go through leaf nodes. This will save you a full path traversal each time.
Internals
Paths
A path is a string representation of any valid JavaScript traversable path within the context of the given object literal.
Some valid path syntaxes:
"a.b.c"
"[0][1][2]"
"a[0].b[1].c[2]"
"a.b[0].c[1][2]"
As we can see, it can get complex quickly.
Remember that a path is relative to the given object literal and is always a string.
Example
literal(obj).method("a.b.c");
The above suggests we want to work with obj.a.b.c
not just a.b.c
.
If you do want to work with a.b.c
you would do this:
literal(a).method("b.c");
As we can see, the path ("b.c."
) is relative to the given object (literal("a")
).
Multiple paths
Some methods also accept multiple paths to operate on.
To specify multiple paths simply define them in the form of an array of path strings (see above for syntax).
Taking the above example of the four individual paths, we can group them into an array as such:
[
"a.b.c",
"[0][1][2]",
"a[0].b[1].c[2]",
"a.b[0].c[1][2]"
]
Whenever paths are passed in as an array, the return value will be an object literal rather than a boolean.
Return Object
The return object will have the following structure:
{
all: [Boolean],
any: [Boolean]
nodes: [Object]
}
It will go through all paths, run the given operation against each path, and store the results in three properties:
all
If the given operation (e.g. check()
) returned true for each path, the all
would be set to true
- and the inverse.
any
If the given operation (e.g. check()
) returned true for some (at least one of the) paths, the any
would be set to true
.
If none of the given paths return true, then this is set to false
.
nodes
This property is a key / value object that stores each path as a key, and if the operation returned true then the value would be true as well.
An simple illustration:
{
all : false,
any : true,
nodes : {
"a.b.c" : false,
"a[0]" : true,
"some.path[2][3]": false
}
}
Node Object
Internally paths are parsed into a node object
that has the following structure:
{
"ok": [Boolean],
"path": [Object],
"leaf": [String]
}
ok
This is a boolean that simply says whether or not the method that returned this node object
executed successfully or not.
path
This is the path object excluding the end point to the literal of the full path.
If we have a full path such as my.little.test, the path
would only contain my.little
.
This is a JavaScript object, so it can be access programmatically without the need of parsing it.
leaf
This is a string representation of the last node of the full path literal.
If we have a full path such as my.little.test, the leaf
would only contain test
.
Object Literal
var funTime = {
map : ["zero", "one", "two", "three", "four", "five", "six",
"seven", "eight", "nine", "ten", "eleven", "twelve"],
periods : {
morning : {
hours : [1, 2, 3, 4, 5, 6, 7, 8, 9]
},
noon : {
hours : [12]
},
afternoon : {
hours : [1, 2, 3, 4, 5]
},
evening : {
hours : [6, 7]
},
night : {
hours : [8, 9, 10, 11]
},
midnight : {
hours : [12]
}
},
cycle : [
"dawn", "twilight", "sunrise", "dusk", "twilight"
],
meridiem : {
am : [ "midnight", "morning"],
pm : [ "noon", "afternoon", "evening", "night"]
},
months : new Array(12)
};
Given the above complex data structure we run the following commands with their corresponding outputs.
Before we begin, let's first alias our identifiers so it becomes less to type.
var literal = require("literal");
var o = literal(funTime);
Now instead of writing literal(funTime).check("some.path")
we can simply write o.check("some.path")
.
Going forward we will use a cached literal in all our examples.
Methods
check(paths, value)
Checks if path or paths exist or match the given value.
paths - optional
undefined
If no
paths
is passed in, it will simply check if the object literal exists.o.check(); // true
string
If
paths
is defined, it will be parsed into anode object
.If
value
is not passed in, then it will check if the givenpath
exists in the context of the given object literal.o.check("some.path.that.does.not.exist"); // false
array
If
paths
is an array of multiple paths and no value is passed in, it will simply check all paths to see if they exist.o.check(["map", "periods", "cycle[0]"]); // return object
value - optional
string
If
value
is passed in, then it will check if the givenpath
's value is the same when it's a string.o.check("cycle[0]", "dawn"); // true
extract(paths, value)
Collects values at the given paths
and assigns them to an object literal where the key is the path and the value is the corresponding value at the given path.
If a given value does not exist, it will default to undefined, unless a value
is given.
paths - required
undefined
If no
paths
is passed in, it will returnfalse
since it doesn't know what you want to extract.o.extract(); // false
string
If
paths
is a single string, it will check if the given path exists in the context of the given object literal and return the value in object literal form.o.extract("cycle[1]"); // { "cycle[1]" : "twilight" }
array
If
paths
is an array, it will check if the given paths exists in the context of the given object literal and return the value in object literal form.o.extract(["cycle[1]", "d.d.d"); // { "cycle[1]" : "twilight", "d.d.d": undefined }
fill(paths, values)
Fills an existing path or paths with a given value or values if the given path or paths are falsy (but not 0
or false
).
paths - required
array
If
paths
is an array of multiple paths, it will check each path for a falsy value and only fill with thevalue
if it is.o.fill(["map", "periods"], "lorem"); // return object
value - required
The value to fill any falsy values with. This includes any newly created paths.
array
If
value
is an array of multiple paths, and thepaths
is an array as well, then thepaths
will be filled incrementally with the values ofvalue
.o.fill("months", ["January", "February"]); // return key value pair
get(paths)
Gets a given value of a given path or paths if they exists.
paths - required
undefined
If no
paths
is passed in, it will return undefined since no value is gotten.o.get(); // undefined
string
If
paths
is a single string, it will check if the given path exists in the context of the given object literal and return the value.o.get("cycle[1]"); // value
array
If
paths
is an array of multiple paths the value at those paths will be retrieved.o.get(["map", "periods", "cycle[0]"]); // return object
plant(paths, value)
Plants a value on a given path or paths only if it doesn't exist.
probe(path)
Alias to get()
.
set(paths, value)
Sets a given value on a given path or paths only if it exists.
paths - required
undefined
If no
paths
is passed in, it will return false since no values were set.o.set(); // false
string
If
value
is not passed in, then it will check if the givenpath
exists in the context of the given object literal.o.set("cycle[1]", "hello"); // node object
array
If
paths
is an array of multiple paths the given value will be assigned only to the paths that exist.o.set(["map", "periods", "cycle[0]"], "hello"); // return object
value - required
anything
This is the value to set a given path to. This can be any valid JavaScript value (e.g. object, array, function, ...)
snip(path)
Remove the last path node (leaf) from the given path. Returns a boolean to indicate if the removal was successful.
o.snip("periods.morning"); // returns true
swap(path, path)
path - required
string
Either of the
path
can be a string to a path that you want to swap with the other.The
path
can be mix and matched with object literals, arrays, and strings.o.swap("periods", "cycle[2]"); // true
The above example will move an entire key value pair data structure and swap places with a simple array item. This is a powerful feature on its own if you manipulate a lot of data.
tap(path, value)
Returns the value of the given path
if it has a truthy value, otherwise return given value
.
path - required
string
The
path
is a string to a path that you want to retrieve its value if it exists.
value - optional
anything
The
value
can be any value to be returned if the givenpath
does not exist or returns a falsy value.
truthy(paths)
Checks given paths
if they exist and if their values are truthy.
paths - required
string
The
paths
is a string to a path that you want to see if it exists and if its value is truthy.array
If
paths
is an array of multiple paths they will all be checked for existence and truthy values.o.truthy(["map", "periods", "cycle[0]"]); // return object
Simple Examples
Here are a few examples based on the above data structure.
Method | Output | Reason |
---|---|---|
o.check(); | true | funTime exists |
o.check("map"); | true | funTime.map exists |
o.check("cycle[2]", "dusk"); | false | funTime.cycle[2] value is not "dusk" |
o.check("meridiem.am[1]"); | true | funTime.meridiem.am[1] exists |
o.check(["map", "cycle"]); | true | funTime.map and funTime.cycle exist |
o.check(["map[3]", "meridiem.am"], "two").all | false | both values are not two |
o.check(["map[3]", "meridiem.am"], "two").any | true | at least one value is two |
o.swap("periods.afternoon", "cycle[2]") | true | values swapped successfully |
o.fill("months") | false | No fill value |
o.fill("months", "Same") | true | All months were filled with "Same" |
o.fill("months", ["Jan", "Feb"]) | true | First two Months were filled with given array |
o.fill("periods.morning.hours", "hello") | false | No items were filled |
o.set("periods.noon.hours[0]", "12:00") | true | Given path exists so value set |
o.plant("a.b.c", "hello") | true | Path is created and value set |
o.plant("months", "hello") | true | Existing path has value changed |
9 years ago