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.
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.
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")
).
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.
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:
If the given operation (e.g. check()
) returned true for each path, the all
would be set to true
- and the inverse.
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
.
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
}
}
Internally paths are parsed into a node object
that has the following structure:
{
"ok": [Boolean],
"path": [Object],
"leaf": [String]
}
This is a boolean that simply says whether or not the method that returned this node object
executed successfully or not.
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.
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
.
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.
Checks if path or paths exist or match the given value.
- 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 a node object
.
If value
is not passed in, then it will check if the given path
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
- string
If value
is passed in, then it will check if the given path
's value is the same when it's a string.
o.check("cycle[0]", "dawn"); // true
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.
- undefined
If no paths
is passed in, it will return false
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 }
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
).
- array
If paths
is an array of multiple paths, it will check each path for a falsy value and only fill with the value
if it is.
o.fill(["map", "periods"], "lorem"); // return object
The value to fill any falsy values with. This includes any newly created paths.
- array
If value
is an array of multiple paths, and the paths
is an array as well, then the paths
will be filled incrementally with the values of value
.
o.fill("months", ["January", "February"]); // return key value pair
Gets a given value of a given path or paths if they exists.
- undefined
If no paths
is passed in, it will return the original root literal.
o.get(); // funTime
- 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
Plants a value on a given path or paths only if it doesn't exist.
Alias to get()
.
Sets a given value on a given path or paths only if it exists.
- 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 given path
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
- anything
This is the value to set a given path to. This can be any valid JavaScript value (e.g. object, array, function, ...)
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
- 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.
Returns the value of the given path
if it has a truthy value, otherwise return given value
.
- string
The path
is a string to a path that you want to retrieve its value if it exists.
- anything
The value
can be any value to be returned if the given path
does not exist or returns a falsy value.
Checks given paths
if they exist and if their values are truthy.
- 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
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 |