Check if every element in one array is in a second array - javascript

I have two arrays and I want to check if every element in arr2 is in arr1. If the value of an element is repeated in arr2, it needs to be in arr1 an equal number of times. What's the best way of doing this?
arr1 = [1, 2, 3, 4]
arr2 = [1, 2]
checkSuperbag(arr1, arr2)
> true //both 1 and 2 are in arr1
arr1 = [1, 2, 3, 4]
arr2 = [1, 2, 5]
checkSuperbag(arr1, arr2)
> false //5 is not in arr1
arr1 = [1, 2, 3]
arr2 = [1, 2, 3, 3]
checkSuperbag(arr1, arr2)
> false //3 is not in arr1 twice

Do you have to support crummy browsers? If not, the every function should make this easy.
If arr1 is a superset of arr2, then each member in arr2 must be present in arr1
var isSuperset = arr2.every(function(val) { return arr1.indexOf(val) >= 0; });
Here's a fiddle
EDIT
So you're defining superset such that for each element in arr2, it occurs in arr1 the same number of times? I think filter will help you do that (grab the shim from the preceding MDN link to support older browsers):
var isSuperset = arr2.every(function (val) {
var numIn1 = arr1.filter(function(el) { return el === val; }).length;
var numIn2 = arr2.filter(function(el) { return el === val; }).length;
return numIn1 === numIn2;
});
Updated Fiddle
END EDIT
If you do want to support older browsers, the MDN link above has a shim you can add, which I reproduce here for your convenience:
if (!Array.prototype.every)
{
Array.prototype.every = function(fun /*, thisp */)
{
"use strict";
if (this == null)
throw new TypeError();
var t = Object(this);
var len = t.length >>> 0;
if (typeof fun != "function")
throw new TypeError();
var thisp = arguments[1];
for (var i = 0; i < len; i++)
{
if (i in t && !fun.call(thisp, t[i], i, t))
return false;
}
return true;
};
}
EDIT
Note that this will be an O(N2) algorithm, so avoid running it on large arrays.

One option is to sort the two arrays, then traverse both, comparing elements. If an element in the sub-bag candidate is not found in the super-bag, the former is not a sub-bag. Sorting is generally O(n*log(n)) and the comparison is O(max(s,t)), where s and t are the array sizes, for a total time complexity of O(m*log(m)), where m=max(s,t).
function superbag(sup, sub) {
sup.sort();
sub.sort();
var i, j;
for (i=0,j=0; i<sup.length && j<sub.length;) {
if (sup[i] < sub[j]) {
++i;
} else if (sup[i] == sub[j]) {
++i; ++j;
} else {
// sub[j] not in sup, so sub not subbag
return false;
}
}
// make sure there are no elements left in sub
return j == sub.length;
}
If the elements in the actual code are integers, you can use a special-purpose integer sorting algorithm (such as radix sort) for an overall O(max(s,t)) time complexity, though if the bags are small, the built-in Array.sort will likely run faster than a custom integer sort.
A solution with potentially lesser time-complexity is to create a bag type. Integer bags are particularly easy. Flip the existing arrays for the bags: create an object or an array with the integers as keys and a repeat count for values. Using an array won't waste space by creating as arrays are sparse in Javascript. You can use bag operations for sub-bag or super-bag checks. For example, subtract the super from the sub candidate and test if the result non-empty. Alternatively, the contains operation should be O(1) (or possibly O(log(n))), so looping over the sub-bag candidate and testing if the super-bag containment exceeds the sub-bag's containment for each sub-bag element should be O(n) or O(n*log(n)).
The following is untested. Implementation of isInt left as an exercise.
function IntBag(from) {
if (from instanceof IntBag) {
return from.clone();
} else if (from instanceof Array) {
for (var i=0; i < from.length) {
this.add(from[i]);
}
} else if (from) {
for (p in from) {
/* don't test from.hasOwnProperty(p); all that matters
is that p and from[p] are ints
*/
if (isInt(p) && isInt(from[p])) {
this.add(p, from[p]);
}
}
}
}
IntBag.prototype=[];
IntBag.prototype.size=0;
IntBag.prototype.clone = function() {
var clone = new IntBag();
this.each(function(i, count) {
clone.add(i, count);
});
return clone;
};
IntBag.prototype.contains = function(i) {
if (i in this) {
return this[i];
}
return 0;
};
IntBag.prototype.add = function(i, count) {
if (!count) {
count = 1;
}
if (i in this) {
this[i] += count;
} else {
this[i] = count;
}
this.size += count;
};
IntBag.prototype.remove = function(i, count) {
if (! i in this) {
return;
}
if (!count) {
count = 1;
}
this[i] -= count;
if (this[i] > 0) {
// element is still in bag
this.size -= count;
} else {
// remove element entirely
this.size -= count + this[i];
delete this[i];
}
};
IntBag.prototype.each = function(f) {
var i;
foreach (i in this) {
f(i, this[i]);
}
};
IntBag.prototype.find = function(p) {
var result = [];
var i;
foreach (i in this.elements) {
if (p(i, this[i])) {
return i;
}
}
return null;
};
IntBag.prototype.sub = function(other) {
other.each(function(i, count) {
this.remove(i, count);
});
return this;
};
IntBag.prototype.union = function(other) {
var union = this.clone();
other.each(function(i, count) {
if (union.contains(i) < count) {
union.add(i, count - union.contains(i));
}
});
return union;
};
IntBag.prototype.intersect = function(other) {
var intersection = new IntBag();
this.each(function (i, count) {
if (other.contains(i)) {
intersection.add(i, Math.min(count, other.contains(i)));
}
});
return intersection;
};
IntBag.prototype.diff = function(other) {
var mine = this.clone();
mine.sub(other);
var others = other.clone();
others.sub(this);
mine.union(others);
return mine;
};
IntBag.prototype.subbag = function(super) {
return this.size <= super.size
&& null !== this.find(
function (i, count) {
return super.contains(i) < this.contains(i);
}));
};
See also "comparing javascript arrays" for an example implementation of a set of objects, should you ever wish to disallow repetition of elements.

No one has posted a recursive function yet and those are always fun. Call it like arr1.containsArray( arr2 ).
Demo: http://jsfiddle.net/ThinkingStiff/X9jed/
Array.prototype.containsArray = function ( array /*, index, last*/ ) {
if( arguments[1] ) {
var index = arguments[1], last = arguments[2];
} else {
var index = 0, last = 0; this.sort(); array.sort();
};
return index == array.length
|| ( last = this.indexOf( array[index], last ) ) > -1
&& this.containsArray( array, ++index, ++last );
};

Using objects (read: hash tables) in stead of sorting should reduce the amortized complexity to O(m+n):
function bagContains(arr1, arr2) {
var o = {}
var result = true;
// Count all the objects in container
for(var i=0; i < arr1.length; i++) {
if(!o[arr1[i]]) {
o[arr1[i]] = 0;
}
o[arr1[i]]++;
}
// Subtract all the objects in containee
// And exit early if possible
for(var i=0; i < arr2.length; i++) {
if(!o[arr2[i]]) {
o[arr2[i]] = 0;
}
if(--o[arr2[i]] < 0) {
result = false;
break;
}
}
return result;
}
console.log(bagContains([1, 2, 3, 4], [1, 3]));
console.log(bagContains([1, 2, 3, 4], [1, 3, 3]));
console.log(bagContains([1, 2, 3, 4], [1, 3, 7]));
Which yields true, false, false.

Found this on github lodash library. This function use built in functions to solve the problem. .includes() , .indexOf() and .every()
var array1 = ['A', 'B', 'C', 'D', 'E'];
var array2 = ['B', 'C', 'E'];
var array3 = ['B', 'C', 'Z'];
var array4 = [];
function arrayContainsArray (superset, subset) {
if (0 === subset.length) {
return false;
}
return subset.every(function (value) {
return (superset.includes(value));
});
}
function arrayContainsArray1 (superset, subset) {
if (0 === subset.length) {
return false;
}
return subset.every(function (value) {
return (superset.indexOf(value) >= 0);
});
}
console.log(arrayContainsArray(array1,array2)); //true
console.log(arrayContainsArray(array1,array3)); //false
console.log(arrayContainsArray(array1,array4)); //false
console.log(arrayContainsArray1(array1,array2)); //true
console.log(arrayContainsArray1(array1,array3)); //false
console.log(arrayContainsArray1(array1,array4)); //false

If arr2 is subset of arr1, then Length of set(arr1 + arr2) == Length of set(arr1)
var arr1 = [1, 'a', 2, 'b', 3];
var arr2 = [1, 2, 3];
Array.from(new Set(arr1)).length == Array.from(new Set(arr1.concat(arr2))).length

Here is my solution:
Array.prototype.containsIds = function (arr_ids) {
var status = true;
var current_arr = this;
arr_ids.forEach(function(id) {
if(!current_arr.includes(parseInt(id))){
status = false;
return false; // exit forEach
}
});
return status;
};
// Examples
[1,2,3].containsIds([1]); // true
[1,2,3].containsIds([2,3]); // true
[1,2,3].containsIds([3,4]); // false

As for another approach you may do as follows;
function checkIn(a,b){
return b.every(function(e){
return e === this.splice(this.indexOf(e),1)[0];
}, a.slice()); // a.slice() is the "this" in the every method
}
var arr1 = [1, 2, 3, 4],
arr2 = [1, 2],
arr3 = [1,2,3,3];
console.log(checkIn(arr1,arr2));
console.log(checkIn(arr1,arr3));

Quick solution here take two arrays if b is longer than it can't be a super set so return false. Then loop through b to see if a contains the element. If so delete it from a and move on if not return false. Worse case scenario is if b is a subset then time will b.length.
function isSuper(a,b){
var l=b.length,i=0,c;
if(l>a.length){return false}
else{
for(i;i<l;i++){
c=a.indexOf(b[i]);
if(c>-1){
a.splice(c,1);
}
else{return false}
}
return true;
}
}
This assumes that inputs will not always be in order and if a is 1,2,3 and b is 3,2,1 it will still return true.

Yet another simple solution is the following:
let a = [1,2,'a',3,'b',4,5]
let b = [1,2,4]
console.log(b.every((i) => a.includes(i)))
Hope it helps

Related

Compare two strings, find the difference, alert the difference and index position where they are not the same [duplicate]

I have two arrays that I need to check the difference upon and return the index of that difference.
For example, I currently have two arrays that get updated when the input's value is changed. The newTags array gets updated whenever there is a new tag within the input, such as #testing. I need to compare the newTags array with the oldTags array and return the index of the difference.
I am currently stringifying both arrays and comparing them that way, although it is unable to return the index of the difference.
var newTags = [];
var oldTags = [];
$input.on('keyup', function () {
var newValue = $input.val();
var pattern = /#[a-zA-Z]+/ig;
var valueSearch = newValue.search(pattern);
if (valueSearch >= 0) {
newTags = newValue.match(pattern);
if ((newTags + "") != (oldTags + "")) {
//Need index of difference here
console.log(newTags, oldTags);
}
oldTags = newTags;
}
});
Working example
You can use a filter to find both the different values and indexes at the same time.
JSFiddle: https://jsfiddle.net/k0uxtnkd/
Array.prototype.diff = function(a) {
var source = this;
return this.filter(function(i) {
if (a.indexOf(i) < 0) {
diffIndexes.push(source.indexOf(i));
return true;
} else {
return false;
}
});
};
var diffIndexes = [];
var newTags = ['a','b','c'];
var oldTags = ['c'];
var diffValues = newTags.diff(oldTags);
console.log(diffIndexes); // [0, 1]
console.log(diffValues); // ['a', 'b']
To convert this to a function instead of add it to the array prototype:
JSFiddle: https://jsfiddle.net/k0uxtnkd/1/
function arrayDiff(a, b) {
return a.filter(function(i) {
if (b.indexOf(i) < 0) {
diffIndexes.push(a.indexOf(i));
return true;
} else {
return false;
}
});
};
var diffIndexes = [];
var newTags = ['a','b','c'];
var oldTags = ['c'];
var diffValues = arrayDiff(newTags, oldTags);
console.log(diffIndexes); // [0, 1]
console.log(diffValues); // ['a', 'b']
You don't need to loop through both arrays, you can simply loop through both simultaneously:
var findDivergence = function (a1, a2) {
var result = [], longerLength = a1.length >= a2.length ? a1.length : a2.length;
for (i = 0; i < longerLength; i++){
if (a1[i] !== a2[i]) {
result.push(i);
}
}
return result;
};
console.log(findDivergence(["a","b","c","d","e","f","g","h","i"], ["a","b","d","r","e","q","g"]));
//outputs [2, 3, 5, 7, 8]
This is significantly more efficient than double-looping or using indexOf (both of which will search the second array many more times than necessary). This also handles cases where the same item shows up more than once in a given array, though if one array is longer than the other and the longer one contains an element that is undefined, that index will count as a match.
for(var i=0; i < newTags.length; i++) {
for(var j=0; j < oldTags.length; j++) {
if(newTags[i] === oldTags[j]) {
console.log("match found");
console.log("Match found for value: " + newTags[i] + " at index in oldTags: " + j + );
}
else{
console.log("match not found");
}
}
}
Using 2 loops you can do a quick check, in the if statements add what you want to happen.
Below is a performance comparison of three common methods to perform the task asked in this question.
const arr1 = ['A', 'B', 'C'];
const arr2 = ['A', 'D', 'C', 'E'];
// Filter indexOf
function diffArray1(a1, a2) {
let aDiffs = [];
a1.filter((i) => {
if (a2.indexOf(i) < 0) {
aDiffs.push(a1.indexOf(i));
}
});
return aDiffs;
};
// Loop indexOf
function diffArray2(a1, a2) {
let aDiffs = [];
for (let i=0; i<a1.length; ++i) {
if (a2.indexOf(a1[i]) < 0) {
aDiffs.push(a1.indexOf(a1[i]));
}
}
return aDiffs;
};
// Loop equality
function diffArray3(a1, a2) {
let aDiffs = [];
for (let i=0; i<a1.length; ++i) {
if (a1[i] !== a2[i]) {
aDiffs.push(i);
}
}
return aDiffs;
};
diffArray1(arr2, arr1); // Returns [1, 3]
diffArray2(arr2, arr1); // Returns [1, 3]
diffArray3(arr2, arr1); // Returns [1, 3]
diffArray3() is the fastest in Chrome v102.0.5005.63 (64-bit) on my system (Intel Core i7-7700HQ 32GB RAM). diffArray1() is about 38% slower and diffArray2() is about 22.5% slower. Here's the test suite:
https://jsbench.me/59l42hhpfs/1
Feel free to fork this and add more methods; please leave the URL of the fork in the comment if you do this.

Iterative solution for flattening n-th nested arrays in Javascript

Can anyone show me an iterative solution for the following problem? I solved it recursively but struggled with an iterative solution. (Facebook Technical Interview Question)
Input: [1, {a: 2}, [3], [[4, 5], 6], 7]
Output: [1, {a: 2}, 3, 4, 5, 6, 7]
Solution must work with n-th nested array elements (i.e. it must still work if someone modifies the array values/placement in the example above)
Recursive solution:
var flatten = function(input) {
var result = [];
input.forEach(function(element) {
result = result.concat(Array.isArray(element) ? flatten(element) : element);
});
return result;
}
Here is one way:
var input = [1, {a: 2}, [3], [[4, 5], 6], 7];
function flatten(input) {
var i, placeHolder = [input], lastIndex = [-1], out = [];
while (placeHolder.length) {
input = placeHolder.pop();
i = lastIndex.pop() + 1;
for (; i < input.length; ++i) {
if (Array.isArray(input[i])) {
placeHolder.push(input);
lastIndex.push(i);
input = input[i];
i = -1;
} else out.push(input[i]);
}
}
return out;
}
flatten(input);
Explanation: If iterating over a nested structure, you just have to remember where you were before by saving the current array and position before moving into the nested array (this is usually taken care of via the stack for recursive solutions).
Note: If you reuse the arrays placeHolder and lastIndex you won't need to keep recreating them every time. Perhaps something like this:
var flatten = function(){
var placeHolder = [], lastIndex = [];
placeHolder.count = 0;
lastIndex.count = 0;
return function flatten(input) {
var i, out = [];
placeHolder[0] = input; placeHolder.count = 1;
lastIndex[0] = -1; lastIndex.count = 1;
while (placeHolder.count) {
input = placeHolder[--placeHolder.count];
i = lastIndex[--lastIndex.count] + 1;
for (; i < input.length; ++i) {
if (Array.isArray(input[i])) {
placeHolder[placeHolder.count++] = input;
lastIndex[lastIndex.count++] = i;
input = input[i];
i = -1;
} else out.push(input[i]);
}
}
return out;
}
}();
This is even faster again (for flat iteration that is), and less garbage collector issues calling it many times. The speed is very close to that of recursive function calling in Chrome, and many times faster than recursion in FireFox and IE.
I recreated Tomalak's tests here since the old jsPerf is broken for editing: https://jsperf.com/iterative-array-flatten-2
How about this?
inp = [1, {a: 2}, [3], [[4, 5], 6], 7]
out = inp;
while(out.some(Array.isArray))
out = [].concat.apply([], out);
document.write(JSON.stringify(out));
Works, but not recommended:
var flatten = function(input) {
return eval("[" + JSON.stringify(input).
replace(/\[/g,"").replace(/\]/g,"") + "]");
}
Here's a solution that flattens in place.
function flatten(arr) {
var i = 0;
if (!Array.isArray(arr)) {
/* return non-array inputs immediately to avoid errors */
return arr;
}
while (i < arr.length) {
if (Array.isArray(arr[i])) {
arr.splice(i, 1, ...arr[i]);
} else {
i++;
}
}
return arr;
}
This solution iterates through the array, flattening each element one level of nesting at a time until it cannot be flattened any more.
function flatten(array){
for(var i=0;i<array.length;i++)
if(Array.isArray(array[i]))
array.splice.apply(array,[i,1].concat(array[i--]));
return array;
}
This in-place solution is faster than Lupe's, now that I've removed all of the inner curly brackets (I inlined the i-- in the concat parameter to do that).
A different iterative algorithm:
function flatten2(input) {
var output = [];
var todo = [input];
var current;
var head;
while(todo.length) {
var current = todo.shift();
if(Array.isArray(current)) {
current = current.slice();
head = current.shift();
if(current.length) {
todo.unshift(current)
}
todo.unshift(head);
} else {
output.push(current);
}
}
return output;
}
Put all elements on a stack.
While the stack is not empty, remove the first element.
If that element is a scalar, add it to the output.
If that element is an array, split it into head (first element) and tail (remaining elements) and add both to the stack.
As Tomalak's JSPerf shows, this is pretty slow.
JSBin
A fairly concise, readable algorithm:
function flatten(input) {
var output = [];
var todo = [input];
var current;
while(todo.length) {
var current = todo.shift();
if(Array.isArray(current)) {
todo.unshift.apply(todo, current)
} else {
output.push(current);
}
}
return output;
}
This version performs better than my other answer, but is still significantly slower than James Wilkins' answer.
JSBin
Tomalak's JSPerf
Here are two approaches, recursive and iterative and their comparison to Array.flat.
Maybe it'll help someone
const arrayToFlatten = [[1], [2, [3]], null, [[{}]], undefined];
// takes an array and flattens it recursively, default depth is 1 (just like Array.flat())
function flattenRecursive(arr, depth = 1) {
let myArray = [];
if (depth === 0){ // if you've reached the depth don't continue
myArray = arr;
} else if(!Array.isArray(arr)) { // add item to array if not an array
myArray.push(arr);
} else { // flatten each item in the array then concatenate
arr.forEach(item => {
const someNewArray = flattenRecursive(item, depth - 1);
myArray = myArray.concat(someNewArray);
});
}
return myArray;
}
// takes an array and flattens it using a loop, default depth is 1 (just like Array.flat())
function flattenIterative(arr, depth = 1) {
let result = arr;
// if an element is an array
while(result.some(Array.isArray) && depth) {
// flatten the array by one level by concating an empty array and result using apply
result = [].concat.apply([], result);
depth--; // track depth
}
return result;
}
console.log(arrayToFlatten.flat(2)); // ES^
console.log(flattenRecursive(arrayToFlatten, 2));
console.log(flattenIterative(arrayToFlatten, 2));
Here's my solution to this:
function flattenList(A) {
let result = []
for (let i=0; i < A.length; i++) {
if (typeof A[i] == "object"){
let item = reduceArray(A[i])
result.push(...item)
}else {
result.push(A[i])
}
}
return result
}
function reduceArray(arr){
while(arr.some(Array.isArray)) {
let item = arr.find(Array.isArray)
let index = arr.indexOf(item)
arr[index] = item[0]
}
return arr
}
Not sure if the "stack" approach was used properly in previous answers. I think it could be simpler, like this:
function flatten(arr) {
const result = [];
const stack = [arr];
while (stack.length) {
const curr = stack.pop();
if (Array.isArray(curr)) {
for (let i = curr.length - 1; i >= 0; i--) {
stack.push(curr[i]);
}
} else {
result.push(curr);
}
}
return result;
}
Not sure why the other answers are so complicated, this can easily be achieved by looping through the array and flattening each entry until it's no longer an array.
const flatten = (arr) => {
for (let i = 0; i < arr.length; i++) {
while (Array.isArray(arr[i])) {
arr.splice(i, 1, ...arr[i]);
}
}
return arr;
}

How to compare two arrays and then return the index of the difference?

I have two arrays that I need to check the difference upon and return the index of that difference.
For example, I currently have two arrays that get updated when the input's value is changed. The newTags array gets updated whenever there is a new tag within the input, such as #testing. I need to compare the newTags array with the oldTags array and return the index of the difference.
I am currently stringifying both arrays and comparing them that way, although it is unable to return the index of the difference.
var newTags = [];
var oldTags = [];
$input.on('keyup', function () {
var newValue = $input.val();
var pattern = /#[a-zA-Z]+/ig;
var valueSearch = newValue.search(pattern);
if (valueSearch >= 0) {
newTags = newValue.match(pattern);
if ((newTags + "") != (oldTags + "")) {
//Need index of difference here
console.log(newTags, oldTags);
}
oldTags = newTags;
}
});
Working example
You can use a filter to find both the different values and indexes at the same time.
JSFiddle: https://jsfiddle.net/k0uxtnkd/
Array.prototype.diff = function(a) {
var source = this;
return this.filter(function(i) {
if (a.indexOf(i) < 0) {
diffIndexes.push(source.indexOf(i));
return true;
} else {
return false;
}
});
};
var diffIndexes = [];
var newTags = ['a','b','c'];
var oldTags = ['c'];
var diffValues = newTags.diff(oldTags);
console.log(diffIndexes); // [0, 1]
console.log(diffValues); // ['a', 'b']
To convert this to a function instead of add it to the array prototype:
JSFiddle: https://jsfiddle.net/k0uxtnkd/1/
function arrayDiff(a, b) {
return a.filter(function(i) {
if (b.indexOf(i) < 0) {
diffIndexes.push(a.indexOf(i));
return true;
} else {
return false;
}
});
};
var diffIndexes = [];
var newTags = ['a','b','c'];
var oldTags = ['c'];
var diffValues = arrayDiff(newTags, oldTags);
console.log(diffIndexes); // [0, 1]
console.log(diffValues); // ['a', 'b']
You don't need to loop through both arrays, you can simply loop through both simultaneously:
var findDivergence = function (a1, a2) {
var result = [], longerLength = a1.length >= a2.length ? a1.length : a2.length;
for (i = 0; i < longerLength; i++){
if (a1[i] !== a2[i]) {
result.push(i);
}
}
return result;
};
console.log(findDivergence(["a","b","c","d","e","f","g","h","i"], ["a","b","d","r","e","q","g"]));
//outputs [2, 3, 5, 7, 8]
This is significantly more efficient than double-looping or using indexOf (both of which will search the second array many more times than necessary). This also handles cases where the same item shows up more than once in a given array, though if one array is longer than the other and the longer one contains an element that is undefined, that index will count as a match.
for(var i=0; i < newTags.length; i++) {
for(var j=0; j < oldTags.length; j++) {
if(newTags[i] === oldTags[j]) {
console.log("match found");
console.log("Match found for value: " + newTags[i] + " at index in oldTags: " + j + );
}
else{
console.log("match not found");
}
}
}
Using 2 loops you can do a quick check, in the if statements add what you want to happen.
Below is a performance comparison of three common methods to perform the task asked in this question.
const arr1 = ['A', 'B', 'C'];
const arr2 = ['A', 'D', 'C', 'E'];
// Filter indexOf
function diffArray1(a1, a2) {
let aDiffs = [];
a1.filter((i) => {
if (a2.indexOf(i) < 0) {
aDiffs.push(a1.indexOf(i));
}
});
return aDiffs;
};
// Loop indexOf
function diffArray2(a1, a2) {
let aDiffs = [];
for (let i=0; i<a1.length; ++i) {
if (a2.indexOf(a1[i]) < 0) {
aDiffs.push(a1.indexOf(a1[i]));
}
}
return aDiffs;
};
// Loop equality
function diffArray3(a1, a2) {
let aDiffs = [];
for (let i=0; i<a1.length; ++i) {
if (a1[i] !== a2[i]) {
aDiffs.push(i);
}
}
return aDiffs;
};
diffArray1(arr2, arr1); // Returns [1, 3]
diffArray2(arr2, arr1); // Returns [1, 3]
diffArray3(arr2, arr1); // Returns [1, 3]
diffArray3() is the fastest in Chrome v102.0.5005.63 (64-bit) on my system (Intel Core i7-7700HQ 32GB RAM). diffArray1() is about 38% slower and diffArray2() is about 22.5% slower. Here's the test suite:
https://jsbench.me/59l42hhpfs/1
Feel free to fork this and add more methods; please leave the URL of the fork in the comment if you do this.

Remove array of indexes from array

John Resig, creator of jQuery created a very handy Array.remove method that I always use it in my projects:
// Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function(from, to) {
var rest = this.slice((to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
};
// Remove the second item from the array
array.remove(1);
// Remove the second-to-last item from the array
array.remove(-2);
// Remove the second and third items from the array
array.remove(1,2);
// Remove the last and second-to-last items from the array
array.remove(-2,-1);
It works great. But I would like to know if it's extendable so that it can take an array of indexes as the first argument?
Otherwise, I will probably make another method that makes use of it:
if (!Array.prototype.removeIndexes) {
Array.prototype.removeIndexes = function (indexes) {
var arr = this;
if (!jQuery)
throw new ReferenceError('jQuery not loaded');
$.each(indexes, function (k, v) {
var index = $.inArray(v, indexes);
if (index !== -1)
arr.remove(index);
});
};
}
If Array.remove() isn't extendable to fit my needs, what do you think about my other solution above?
I think this is what you are looking for (It works with negative index too) :
if (!Array.prototype.removeIndexes) {
Array.prototype.removeIndexes = function (indexes) {
var arr = this;
if (!jQuery) throw new ReferenceError('jQuery not loaded');
var offset = 0;
for (var i = 0; i < indexes.length - 1; i++) {
if (indexes[i] < 0)
indexes[i] = arr.length + indexes[i];
if (indexes[i] < 0 || indexes[i] >= arr.length)
throw new Error('Index out of range');
}
indexes = indexes.sort();
for (var i = 0; i < indexes.length - 1; i++) {
if (indexes[i + 1] == indexes[i])
throw new Error('Duplicated indexes');
}
$.each(indexes, function (k, index) {
arr.splice(index - offset, 1);
offset++;
});
return arr;
};
}
var a = ['a', 'b', 'c', 'd', 'e', 'f'];
var ind = [3, 2, 4];
a.removeIndexes(ind);
console.log(a.join(', '));
// returns : a, b, f
See fiddle
This version should work. It modifies the original array. If you prefer to return a new array without modifying the original, use the commented out initializer of result and add return result at the end of the function.
Array.prototype.removeIndexes = function(indices) {
// make sure to remove the largest index first
indices = indices.sort(function(l, r) { return r - l; });
// copy the original so it is not changed
// var result = Array.prototype.slice.call(this);
// modify the original array
var result = this;
$.each(indices, function(k, ix) {
result.splice(ix, 1);
});
}
> [0, 1, 2, 3, 4, 5, 6, 7, 8].removeIndexes([4, 5, 1]);
> [0, 2, 3, 6, 7, 8]
How about
Array.prototype.remove = function (indexes) {
if(indexes.prototype.constructor.name == "Array") {
// your code to support indexes
} else {
// the regular code to remove single or multiple indexes
}
};

Compare 2 arrays which returns difference

What's the fastest/best way to compare two arrays and return the difference? Much like array_diff in PHP. Is there an easy function or am I going to have to create one via each()? or a foreach loop?
I know this is an old question, but I thought I would share this little trick.
var diff = $(old_array).not(new_array).get();
diff now contains what was in old_array that is not in new_array
Working demo http://jsfiddle.net/u9xES/
Good link (Jquery Documentation): http://docs.jquery.com/Main_Page {you can search or read APIs here}
Hope this will help you if you are looking to do it in JQuery.
The alert in the end prompts the array of uncommon element Array i.e. difference between 2 array.
Please lemme know if I missed anything, cheers!
Code
var array1 = [1, 2, 3, 4, 5, 6];
var array2 = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var difference = [];
jQuery.grep(array2, function(el) {
if (jQuery.inArray(el, array1) == -1) difference.push(el);
});
alert(" the difference is " + difference);​ // Changed variable name
use underscore as :
_.difference(array1,array2)
var arrayDiff = function (firstArr, secondArr) {
var i, o = [], fLen = firstArr.length, sLen = secondArr.length, len;
if (fLen > sLen) {
len = sLen;
} else if (fLen < sLen) {
len = fLen;
} else {
len = sLen;
}
for (i=0; i < len; i++) {
if (firstArr[i] !== secondArr[i]) {
o.push({idx: i, elem1: firstArr[i], elem2: secondArr[i]}); //idx: array index
}
}
if (fLen > sLen) { // first > second
for (i=sLen; i< fLen; i++) {
o.push({idx: i, 0: firstArr[i], 1: undefined});
}
} else if (fLen < sLen) {
for (i=fLen; i< sLen; i++) {
o.push({idx: i, 0: undefined, 1: secondArr[i]});
}
}
return o;
};
/** SUBTRACT ARRAYS **/
function subtractarrays(array1, array2){
var difference = [];
for( var i = 0; i < array1.length; i++ ) {
if( $.inArray( array1[i], array2 ) == -1 ) {
difference.push(array1[i]);
}
}
return difference;
}
You can then call the function anywhere in your code.
var I_like = ["love", "sex", "food"];
var she_likes = ["love", "food"];
alert( "what I like and she does't like is: " + subtractarrays( I_like, she_likes ) ); //returns "Naughty"!
This works in all cases and avoids the problems in the methods above. Hope that helps!
In this way you don't need to worry about if the first array is smaller than the second one.
var arr1 = [1, 2, 3, 4, 5, 6,10],
arr2 = [1, 2, 3, 4, 5, 6, 7, 8, 9];
function array_diff(array1, array2){
var difference = $.grep(array1, function(el) { return $.inArray(el,array2) < 0});
return difference.concat($.grep(array2, function(el) { return $.inArray(el,array1) < 0}));;
}
console.log(array_diff(arr1, arr2));
if you also want to compare the order of the answer you can extend the answer to something like this:
Array.prototype.compareTo = function (array2){
var array1 = this;
var difference = [];
$.grep(array2, function(el) {
if ($.inArray(el, array1) == -1) difference.push(el);
});
if( difference.length === 0 ){
var $i = 0;
while($i < array1.length){
if(array1[$i] !== array2[$i]){
return false;
}
$i++;
}
return true;
}
return false;
}
The short version can be like this:
const diff = (a, b) => b.filter((i) => a.indexOf(i) === -1);
result:
diff(['a', 'b'], ['a', 'b', 'c', 'd']);
["c", "d"]
Array operations like this is not jQuery's strongest point. You should consider a library such as Underscorejs, specifically the difference function.
This should work with unsorted arrays, double values and different orders and length, while giving you the filtered values form array1, array2, or both.
function arrayDiff(arr1, arr2) {
var diff = {};
diff.arr1 = arr1.filter(function(value) {
if (arr2.indexOf(value) === -1) {
return value;
}
});
diff.arr2 = arr2.filter(function(value) {
if (arr1.indexOf(value) === -1) {
return value;
}
});
diff.concat = diff.arr1.concat(diff.arr2);
return diff;
};
var firstArray = [1,2,3,4];
var secondArray = [4,6,1,4];
console.log( arrayDiff(firstArray, secondArray) );
console.log( arrayDiff(firstArray, secondArray).arr1 );
// => [ 2, 3 ]
console.log( arrayDiff(firstArray, secondArray).concat );
// => [ 2, 3, 6 ]

Categories

Resources