New Arrayzing Command: merge()
Some nights, as I lay awake in bed, trying to sleep, I think about Arrayzing workflows. Lately, I've been thinking of the different ways users might use Arrayzing. In particular, I've been thinking of how a user could edit a subset of elements in a zing without disturbing other elements. The result of this thoughtstream is a new Arrayzing command: merge()
.
In short, the merge()
command allows you to merge your current zing with the previous zing in your stack. What's the stack, you ask?
Recall that, by default, Arrayzing a functional API, meaning that (unless you're using a mutator command) your zing is never modified. Instead a new one is returned. So, if we do this...
_(1, 4, 9, 144).gteq(4).map(Math.sqrt);
...we have in fact created 3 zings, not one. Although it seems wasteful to keep making new zings, it turns out to be pretty useful. For one, it allows us to step backwards using the undo()
command:
_(1, 4, 9, 144) // [1, 4, 9, 144]
.gteq(4) // [4, 9, 144]
.map(Math.sqrt) // [2, 3, 12]
.undo() // [4, 9, 144]
.undo() // [1, 4, 9, 144]
.undo(); // []
(Aside: For those familiar with jQuery, the undo()
command is the same as the end() command from that library).
Keeping a history (or stack) also allows us to combine the past with the present using the andSelf()
command:
var double = function (x) { return x * 2; };
_(1, 2, 3) // [1, 2, 3]
.map(double) // [2, 4, 6]
.andSelf(); // [2, 4, 6, 1, 2, 3]
But what else? Well, consider this situation: What if we wanted to modify only a subset of a zing? For example, imagine we a list of monetary values and we wanted to make sure they all had the "$" character in front of them. Let's try and do that without using merge()
:
var fn = function(e)
{
if (!e.startsWith("$"))
return "$" + e;
};
_("$1.00", "$1.99", "100.00", "$5").map(fn);
That solution is OK. It's pretty simple but it still requires a lot of typing and it's not really using Arrayzing for much (just the map()
command). Let's see if we can reduce the number of keystrokes and make better use of Arrayzing's capabilities by using the new merge()
command:
_("$1.00", "$1.99", "100.00", "$5")
.not(/^\$/).prepend$("$").merge();
So what just happened there? What does it all mean? Let's look at that again, and track the contents of the zing:
_("$1.00", "$1.99", "100.00", "$5")
.not(/^\$/) // [ "100.00" ]
.prepend$("$") // [ "$100.00" ]
.merge();
// [ "$1.00", "$1.99", "$100.00", "$5" ]
In line 1, we create the Arrayzing (nothing special here). In the following line, we filter out all strings that don't begin with "$" using a regular expression. Things get a little more interesting on line 3, where we use the mutator version of prepend()
to modify the filtered array in-place. Finally, on line 4, we use the merge()
command which, basically puts the originally filtered monetary value back into the preceding zing.
So why did we use the prepend$()
instead of prepend()
? The reason for that is because using prepend()
would have created another level of history. For example, here is what would happen if we had used prepend()
instead of prepend$()
:
_("$1.00", "$1.99", "100.00", "$5")
.not(/^\$/) // [ "100.00" ]
.prepend("$") // [ "$100.00" ]
.merge(); // [ "$100.00" ]
Why did that happen? The answer to that question lies in how merge()
merges. Let's start by looking at line 2 of the last code snippet. In that line, we filtered out all strings that did not start with a "$" character. When Arrayzing returns a new zing that contains only the elements that start with a "$", it also remembers where the new elements came from. So in the case of the not()
function on line 2, Arrayzing remembers that "100.00" came from index 2 of the zing in line 1 (that is, the zing _("$1.00", "$1.99", "100.00", "$5")). The merge()
command only operates on the current zing and the one immediately previous to it on the stack.
With that in mind, let's look at line 3. On line 3, we run the prepend()
command, which creates and returns a new zing that contains the value "$100.00". What's more, it also remembers that the value "$100.00" was derived from "100.00" (index 0) in the zing immediately previous to it in the stack (that is, the zing _("100.00")). Thus, when merged, it simply overwrites the value it derived from, which is the 0th index of the zing [ "100.00" ]... which results in the zing [ "$100.00" ]. You follow all that?
This concept is easier to understand by annotating the previous code snippet:
var zing1 = _("$1.00", "$1.99", "100.00", "$5");
// [ "$1.00", "$1.99", "100.00", "$5" ] index 0-3 come from nothing
var zing2 = zing1.not(/^\$/);
// [ "100.00" ] index 0 comes from index 2 in zing1
var zing3 = zing2.prepend("$");
// [ "$100.00" ] index 0 comes from index 0 in zing2
var zing4 = zing3.merge();
// [ "$100.00" ] copy index 0 from zing3 over index 0 in zing2
Now, with that more or less explained, let's go on to one final item: What happens when we try to merge a zing that we've added to or removed from. For example, if we added elements like this:
_("str", 2, 3, 4, "a", "b").numbers().add$(7).merge();
What would be in the zing returned by this sequence of functions? In this case, when merge()
is called, Arrayzing would not know where to merge the 7 element (since it only remembers the original indices of elements that directly derived from the previous zing). Arrayzing deals with this scenario by simply adding all elements that it does not have an index for to the end of the previous zing in the stack. Here's what that same snippet would look like if we documented each step:
_("str", 2, 3, 4, "a", "b")
.numbers() // [ 2, 3, 4 ]
.add$(7) // [ 2, 3, 4, 7 ]
.merge(); // [ "str", 2, 3, 4, "a", "b", 7 ]
How about if we removed elements? If we were to remove elements, they would simply not be merged back into the previous zing on the stack. For example:
_("str", 2, 3, 4, "a", "b")
.numbers() // [ 2, 3, 4 ]
.set$(0, 4) // [ 4, 3, 4 ]
.set$(2, 7) // [ 4, 3, 7 ]
.removeAt$(0) // [ 3, 7 ]
.merge(); // [ "str", 2, 3, 7, "a", "b" ]
As you can see, the merge()
command is a powerful addition to Arrayzing. It allows us to select a subset of the elements, transform them, and then re-add them to our original zing.