Ruby-like difference between two arrays in JavaScript
Ruby has a nifty feature that allows you to “subtract” two arrays. e.g.
Fruit = ["Apple", "Kinnow", "Mango", "Orange"]
Citrus = ["Lemon", "Kinnow", "Orange", "Tangerine"]
Then Fruit – Citrus gives:
["Apple", "Mango"]
Notice that elements in Citrus not in Fruit (Lemon, Tangerine) are not in the difference.
Now I needed something similar in Javascript. So I started by pushing my luck:
var Fruit = ["Apple", "Kinnow", "Mango", "Orange"];
var Citrus = ["Lemon", "Kinnow", "Orange", "Tangerine"];
var Diff = Fruit - Citrus;
Depending on where you are running this code, Diff will be 0 or NaN. This meant that I would have to come up with something of my own. I figured I’ll put javascript’s regular expressions to some use and came up with this:
function diffArrays (A, B) {
var strA = ":" + A.join("::") + ":";
var strB = ":" + B.join(":|:") + ":";
var reg = new RegExp("(" + strB + ")","gi");
var strDiff = strA.replace(reg,"").replace(/^:/,"").replace(/:$/,"");
var arrDiff = strDiff.split("::");
return arrDiff;
}
diffArrays(Fruit, Citrus) gives:
["Apple", "Mango"]
The thing with dynamically typed languages is that you can take even numeric arrays through this regular expression rigmarole and get a correct result back without anyone complaining:
var Natural = [1,2,3,4,5,6];
var Prime = [1,2,3,5,7];
diffArrays(Natural, Prime) gives:
[4, 6]
A brief explanation of how this works:
I start by converting the first array to a string delimited with two colons, :: – the choice was arbitrary. The fact that regular expressions don’t treat a colon any specially helps. I then prefix and suffix the resulting string with another colon. The idea here is to have each element “surrounded” by its own pair of colons. For instance, [1,2,3,4,5,6] becomes :1::2::3::4::5::6:
The array to be subtracted from it – say [1,2,3,5] is converted to the form – :1:|:2:|:3:|:5: which I then use to create a regular expression. The pipe | has a special meaning in the regular expression world and will cause :1: or :2: or :3: or :5: to be matched.
Calling strA.replace with our just-crafted regular expression replaces :1:, :2:, :3: and :5: with “” giving us :4::6:, which I strip clean of the leading and trailing colons through another couple of replace calls. Finally, spitting the string on :: gives the delta array!
Now this implementation is certainly not going to win me any prizes in speed/elegance pageants, but I think this somewhat awkward application of regular expression was something worth sharing!
On a somewhat related note, a special thanks goes out to Steve Yegge for his wonderful js2-mode and ejacs. The latter especially comes in handy when you are just “doodling” around on JavaScript problems like these.