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 ]
Related
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.
My application creates a JavaScript object, like the following:
myObj= {1:[Array-Data], 2:[Array-Data]}
But I need this object as an array.
array[1]:[Array-Data]
array[2]:[Array-Data]
So I tried to convert this object to an array by iterating with $.each through the object and adding the element to an array:
x=[]
$.each(myObj, function(i,n) {
x.push(n);});
Is there an better way to convert an object to an array or maybe a function?
If you are looking for a functional approach:
var obj = {1: 11, 2: 22};
var arr = Object.keys(obj).map(function (key) { return obj[key]; });
Results in:
[11, 22]
The same with an ES6 arrow function:
Object.keys(obj).map(key => obj[key])
With ES7 you will be able to use Object.values instead (more information):
var arr = Object.values(obj);
Or if you are already using Underscore/Lo-Dash:
var arr = _.values(obj)
var myObj = {
1: [1, 2, 3],
2: [4, 5, 6]
};
var array = $.map(myObj, function(value, index) {
return [value];
});
console.log(array);
Output:
[[1, 2, 3], [4, 5, 6]]
Simply do
Object.values(obj);
That's all!
I think you can use for in but checking if the property is not inerithed
myObj= {1:[Array-Data], 2:[Array-Data]}
var arr =[];
for( var i in myObj ) {
if (myObj.hasOwnProperty(i)){
arr.push(myObj[i]);
}
}
EDIT - if you want you could also keep the indexes of your object, but you have to check if they are numeric (and you get undefined values for missing indexes:
function isNumber(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
myObj= {1:[1,2], 2:[3,4]}
var arr =[];
for( var i in myObj ) {
if (myObj.hasOwnProperty(i)){
if (isNumber(i)){
arr[i] = myObj[i];
}else{
arr.push(myObj[i]);
}
}
}
If you know the maximum index in you object you can do the following:
var myObj = {
1: ['c', 'd'],
2: ['a', 'b']
},
myArr;
myObj.length = 3; //max index + 1
myArr = Array.prototype.slice.apply(myObj);
console.log(myArr); //[undefined, ['c', 'd'], ['a', 'b']]
Since ES5 Object.keys() returns an array containing the properties defined directly on an object (excluding properties defined in the prototype chain):
Object.keys(yourObject).map(function(key){ return yourObject[key] });
ES6 takes it one step further with arrow functions:
Object.keys(yourObject).map(key => yourObject[key]);
Nowadays, there is a simple way to do this : Object.values().
var myObj = {
1: [1, 2, 3],
2: [4, 5, 6]
};
console.log(Object.values(myObj));
Output:
[[1, 2, 3], [4, 5, 6]]
This doesn't required jQuery, it's been defined in ECMAScript 2017.
It's supported by every modern browser (forget IE).
The best method would be using a javascript -only function:
var myArr = Array.prototype.slice.call(myObj, 0);
x = [];
for( var i in myObj ) {
x[i] = myObj[i];
}
ECMASCRIPT 5:
Object.keys(myObj).map(function(x) { return myObj[x]; })
ECMASCRIPT 2015 or ES6:
Object.keys(myObj).map(x => myObj[x])
How about jQuery.makeArray(obj)
This is how I did it in my app.
ES8 way made easy:
The official documentation
const obj = { x: 'xxx', y: 1 };
let arr = Object.values(obj); // ['xxx', 1]
console.log(arr);
The solving is very simple
var my_obj = {1:[Array-Data], 2:[Array-Data]}
Object.keys(my_obj).map(function(property_name){
return my_obj[property_name];
});
Fiddle Demo
Extension to answer of bjornd .
var myObj = {
1: [1, [2], 3],
2: [4, 5, [6]]
}, count = 0,
i;
//count the JavaScript object length supporting IE < 9 also
for (i in myObj) {
if (myObj.hasOwnProperty(i)) {
count++;
}
}
//count = Object.keys(myObj).length;// but not support IE < 9
myObj.length = count + 1; //max index + 1
myArr = Array.prototype.slice.apply(myObj);
console.log(myArr);
Reference
Array.prototype.slice()
Function.prototype.apply()
Object.prototype.hasOwnProperty()
Object.keys()
If you want to keep the name of the object's properties as values. Example:
var fields = {
Name: { type: 'string', maxLength: 50 },
Age: { type: 'number', minValue: 0 }
}
Use Object.keys(), Array.map() and Object.assign():
var columns = Object.keys( fields ).map( p => Object.assign( fields[p], {field:p} ) )
Result:
[ { field: 'Name', type: 'string', maxLength: 50 },
{ field: 'Age', type: 'number', minValue: 0 } ]
Explanation:
Object.keys() enumerates all the properties of the source ; .map() applies the => function to each property and returns an Array ; Object.assign() merges name and value for each property.
I made a custom function:
Object.prototype.toArray=function(){
var arr=new Array();
for( var i in this ) {
if (this.hasOwnProperty(i)){
arr.push(this[i]);
}
}
return arr;
};
After some tests, here is a general object to array function convertor:
You have the object:
var obj = {
some_key_1: "some_value_1"
some_key_2: "some_value_2"
};
The function:
function ObjectToArray(o)
{
var k = Object.getOwnPropertyNames(o);
var v = Object.values(o);
var c = function(l)
{
this.k = [];
this.v = [];
this.length = l;
};
var r = new c(k.length);
for (var i = 0; i < k.length; i++)
{
r.k[i] = k[i];
r.v[i] = v[i];
}
return r;
}
Function Use:
var arr = ObjectToArray(obj);
You Get:
arr {
key: [
"some_key_1",
"some_key_2"
],
value: [
"some_value_1",
"some_value_2"
],
length: 2
}
So then you can reach all keys & values like:
for (var i = 0; i < arr.length; i++)
{
console.log(arr.key[i] + " = " + arr.value[i]);
}
Result in console:
some_key_1 = some_value_1
some_key_2 = some_value_2
Edit:
Or in prototype form:
Object.prototype.objectToArray = function()
{
if (
typeof this != 'object' ||
typeof this.length != "undefined"
) {
return false;
}
var k = Object.getOwnPropertyNames(this);
var v = Object.values(this);
var c = function(l)
{
this.k = [];
this.v = [];
this.length = l;
};
var r = new c(k.length);
for (var i = 0; i < k.length; i++)
{
r.k[i] = k[i];
r.v[i] = v[i];
}
return r;
};
And then use like:
console.log(obj.objectToArray);
You can create a simple function to do the conversion from object to array, something like this can do the job for you using pure javascript:
var objectToArray = function(obj) {
var arr = [];
if ('object' !== typeof obj || 'undefined' === typeof obj || Array.isArray(obj)) {
return obj;
} else {
Object.keys(obj).map(x=>arr.push(obj[x]));
}
return arr;
};
or this one:
var objectToArray = function(obj) {
var arr =[];
for(let o in obj) {
if (obj.hasOwnProperty(o)) {
arr.push(obj[o]);
}
}
return arr;
};
and call and use the function as below:
var obj = {1:'a', 2:'b', 3:'c', 4:'d', 5:'e'};
objectToArray(obj); // return ["a", "b", "c", "d", "e"]
Also in the future we will have something called Object.values(obj), similar to Object.keys(obj) which will return all properties for you as an array, but not supported in many browsers yet...
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.
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
}
};
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