Check if the values of one array are in another - javascript

I need to check if array number 2 contains all of the values in array number 1. I am not aware of any method that does this so I developed one that works, I think. Is there a better way to do this, is this a good solution?
var contains = function(a1, a2){
var cCount = 0;
for (var i=0; i<a1.length; i++){
for (var j=0; j<a2.length; j++){
if (a1[i] == a2[j]){
cCount++;
}}}
if (cCount == a1.length){
return true;
}
};

You could check sizes before starting. return false when one is not present instead using a counter. and return true if it reach the end. And use indexof instead looping through a2 every time.
var contains = function(a1, a2){
if (a1.length>a2.length) return false;
for (var i=0; i<a1.length; i++){
if (a2.indexOf(a1[i])<0) return false;
}
return true;
}

just a bit simplified code:
function contains(array1, array2){
var found = false;
for (i in array1) {
for (j in array2) {
if (array1[i] == array2[j]) {
found = true;
}
}
if (!found) return false;
}
return true;
}
another solution I don't really like it but it's shorter...
function contains (arr1, arr2) {
var specialChar = "|"; // Use any char or a sequence that won't exist in values.
var str = specialChar + arr2.join(specialChar) + specialChar;
for (i in arr1) if (str.indexOf(specialChar + arr1[i] + specialChar) == -1) return false;
return true;
}

Your solution is O(n * n) i.e. order n-squared.
You could sort the arrays first and then sequentially check the elements in the sorted arrays for a match. This will give you an O(n log n) solution. Also you can short circuit the check by ensuring that the size of array2 <= the size of array1.
Evidently this only matters if your arrays are sufficiently big.

You can do this in O(n) if you have a 3rd object that you use to keep track of the items that have been seen already. This assumes that the lookup in seen is O(1) (which presumably it is - What's the big O for JavaScript's array when used as a hash?)
var seen = {};
arr2.forEach(function(el) {
seen[el] = true;
});
var allContained = true;
arr1.forEach(function(el) {
if ( allContained && !seen[el] ) {
allContained = false;
}
});
return allContained;

I'd personally use the Array.every() method (though this is, of course, dependent upon a browser that implements this method) in conjunction with Array.indexOf(), which would result in something akin to the following:
var contains = function(needle, haystack){
return needle.every(function(a){
return haystack.indexOf(a) > -1;
});
};
Combining that with the approach you've already produced (testing for browser-support):
var contains = function(needle, haystack){
if ([].every){
return needle.every(function(a){
return haystack.indexOf(a) > -1;
});
}
else {
var result = true;
for (var i = 0, len = needle.length; i < len; i++){
if (haystack.indexOf(needle[i]) === -1) {
return false;
}
}
return result;
}
}
var a1 = [1,2,3],
a2 = [1,2,3,4];
console.log(contains(a1, a2));
JS Fiddle demo.
Note that the else code isn't optimised, it's simply there to demonstrate the code. Having said that, there is a shim for Array.every() at the MDN page (in the references, below) that might make things easier.
References:
Array.prototype.every().
Array.prototype.indexOf().

Related

Array Splice - Javascript

Its a very small issue and for the life of me I can't figure out what it is. My brain has locked itself from thinking. I need someone else to have a look this code.
The output of the code should be: [1,0,0,0]
UPDATE:
The function should be able to read an array of numbers and if it finds any zeros within the array it should move them to the end of the array.
The output of the code keeps coming as: [0,1,0,0]
var arrNum = [0,0,0,1];
function test() {
for(var i=0; i<arrNum.length; i++){
if(arrNum[i] == 0){
arrNum.splice(i,1)
arrNum.splice(arrNum.length, 1, 0)
}
}
return alert(arrNum)
}
Here is a working plunker.
Apologies for this, I know the issue is something very small but my brain has stopped working now and I need a fresh pair of eyes.
With the way you have it written, you need to loop in the reverse order. You end up skipping indexes when you remove the index. Looping in the reverse direction keeps you from skipping them.
for(var i=arrNum.length-1; i>=0; i--){
You can use unshift() to insert at beginning of an array and push() to the end...
var arrNum = [0,0,0,1];
var output = [];
function test()
{
for(var i=0; i<arrNum.length; i++)
{
if(arrNum[i] == 0)
output.push(0);
else
output.unshift(arrNum[i]);
}
return alert(output)
}
var arrNum = [0,0,0,1];
var result = [];
arrNum.forEach(function(v) {
!!v ? result.unshift(v) : result.push(v);
});
console.log(result);
You are iterating with index i = 0,1,2,3 and at the same time removing first elements of array. So your iteration can not see the 1, it jumps over as it is moved to already iterated index. Easiest would be to just reverse the loop to bypass the issue.
var arrNum = [0,0,0,1];
function test() {
for(var i= arrNum.length; i >= 0; i--){
if(arrNum[i] == 0){
arrNum.splice(i,1)
arrNum.splice(arrNum.length, 1, 0)
}
}
return alert(arrNum)
}
Prefer built-in functions every time possible.
var output = [];
[0,0,0,1].forEach(function(num) {
if(num == 0) output.push(0);
else output.unshift(num)
})
Why don't you use a temporary array to help? The problem with your code is that the splice() function modifies the original array, and you are doing it inside the loop.
The code below produces what you need:
var arrNum = [0,0,0,1];
var arrResult = new Array();
function test() {
for(var i=arrNum.length-1; i>=0; i--)
{
arrResult.push(arrNum[i]);
}
arrNum = arrResult;
return alert(arrNum);
}
With another array to store the new values, you gain flexibility to do whatever you need with the data of the first array.
A nice little way using Objects - busy learning them so just posting a variation of deligation
var methods = {
moveZero: function(arr){
//console.log(arr);
var newArr = [];
for(var i = 0; i < arr.length; i++){
if(arr[i] === 0){
newArr.push(arr[i]);
}else{
newArr.unshift(arr[i]);
}
}
console.log(newArr);
}
}
var arrNum = Object.create(methods);
arrNum.moveZero([0,0,50,56,85,0,0,43,10,0,1]);
JSFiddle - https://jsfiddle.net/ToreanJoel/qh0xztgc/1/
The problem was you are modifying an array while looping over it in if statement.
Here is a working plunker of your example.
var len = arrNum.length;
var index = 0;
while(len) {
if(arrNum[index] == 0) {
arrNum.splice(index,1);
arrNum.push(0);
} else {
++index;
}
--len;
}
As the operation you want to do is actually sorting, for readability and compactness of code maybe you should be doing this instead:
var arrNum = [0,1,0,0];
arrNum.sort(function(a, b) {
return a == 0 ? 1 : 0;
});
It can contain any number and will keep order of others than 0

JS Return array based on the comparison of two arrays with nested elements

Using Angular and trying to return result based on the comparison of two arrays. Here is what I have:
$scope.fbFriends = [{"id":1234,"name":'bob'},
{"id":4567,"name":'john'},
{"id":8910,"name":'totoro'}];
$scope.appFriends = [{"id":1,"name":'bob',"fb_id":1234},
{"id":2,"name":'john',"fb_id":4567}];
I would like to filter the friends that exist in both and return only the ones from fbFriends that don't exist in appFriends.
This is what I did but it doesn't work it returns way too many times the same.
$scope.not_friends = [];
$scope.filtered = [];
for (var i=0; i < $scope.fbFriends.length; i++) {
for (var j=0; j < $scope.appFriends.length; j++) {
if ($scope.fbFriends[i].id !== $scope.appFriends[j].fb_id) {
$scope.not_friends = $scope.fbFriends[i];
$scope.filtered.push($scope.not_friends);
}
}
};
console.log($scope.filtered);
What is wrong in this approach?
Bonus, could I integrate that in a filter and use it in a ng-repeat of fbFriends ?
Thanks!!
The problem is the if block: you have to check for equality and then - since you are searching for friends that are NOT contained in the fbFriends - remove the object.
I've rewritten your code and it seems to work properly.
$scope.fbFriends = [{"id":1234,"name":'bob'},
{"id":4567,"name":'john'},
{"id":8910,"name":'totoro'}];
$scope.appFriends = [{"id":1,"name":'bob',"fb_id":1234},
{"id":2,"name":'john',"fb_id":4567}];
for (var i=0; i < $scope.appFriends.length; i++) {
// we parse the 'reference array'
for (var j=0; j < $scope.fbFriends.length; j++) {
// we check every value inside the 'container array'
if ($scope.fbFriends[j].id == $scope.appFriends[i].fb_id) {
// the element is alredy inside the appFriends
$scope.fbFriends.splice(j,1);
}
}
}
console.log($scope.fbFriends);
For further reference see an algorithm book (Mine suggestion is Introduction to Algorithms by Thomas H. Cormen, but anyone will do.)
Here's a generic set difference operation
difference = function(a, b, eq) {
return a.filter(function(x) {
return b.every(function(y) {
return !eq(x, y)
});
});
}
where a, b are arrays and eq - an equality function.
And this is how to apply it to your problem:
fbFriends = [{"id":1234,"name":'bob'},
{"id":4567,"name":'john'},
{"id":8910,"name":'totoro'}];
appFriends = [{"id":1,"name":'bob',"fb_id":1234},
{"id":2,"name":'john',"fb_id":4567}];
difference = function(a, b, eq) {
return a.filter(function(x) {
return b.every(function(y) {
return !eq(x, y)
});
});
}
onlyFb = difference(fbFriends, appFriends, function(f, a) {
return f.id === a.fb_id
});
document.write(JSON.stringify(onlyFb))

Find the index of an array element without using built in indexOf function

I am fairly new to JS and have a project to find the index of an array element, without using indexOf built in function. I have tried to search for solutions, but all I get are examples of the aforementioned built in function, which I already know how to use. Can anyone provide a simple example for me to go on?
My mind goes towards something like this, but please note I am new to this and it is an academic exercise that I really want to understand:
var index;
var target = 10;
for(var val in collection){
if(collection[val] === target){
index = val;
}
return index;
}
This attempt is almost correct. You seem to already understand of what is required: loop over the elements of the array until you find a match, and then return the index of that match.
You've made a few mistakes, though. Let's walk through some improvements, step by step.
Your return statement is inside the loop, but outside of the if. You only want to return if you're found a match. Currently, you always return after the first iteration of the loop!
function myIndexOf(collection, target) {
var index;
for(var val in collection){
if(collection[val] === target){
index = val;
return index;
}
}
}
There is no need for a separate index variable. You can return val as soon as you determine it's the correct answer.
function myIndexOf(collection, target) {
for(var val in collection){
if(collection[val] === target){
return val;
}
}
}
You should loop using a numeric for loop, not a for-in loop. for-in loops are not guaranteed to have a set order, so you may not always get the lowest index that is a match. (Also, for-in could match on non-numeric property names, which might not be what you want.)
function myIndexOf(collection, target) {
for(var val=0; val<collection.length; val++){
if(collection[val] === target){
return val;
}
}
}
To act just like indexOf, you could return -1 in the case that you don't find a match.
function myIndexOf(collection, target) {
for(var val=0; val<collection.length; val++){
if(collection[val] === target){
return val;
}
}
return -1;
}
Note: for..in should not be used to iterate over an Array where the
index order is important.
for..in - JavaScript | MDN
var find_index = function(collection, item) {
for (var i = 0; i < collection.length; ++i) {
if (collection[i] === item) {
return i;
}
}
};
find_index([5,4,3,2,1], 5)
When looping through an array you should use a simple for loop from index 0 to Length-1 of your array. Don't use for...in because it can iterate properties of the array that aren't the actual contents of the cells and the order is not guaranteed. You want to find the first match to have the same behavior as .indexOf. So the correct implementation would look something like this:
function myIndexOf(array, target) {
for (var i=0; i < array.length; i++) {
if (array[i] === target) {
return i;
}
}
// item was not found
return -1;
}
You can use in statement to iterate an array, assumings keys are values. Here val will be 0, 1, 2... so you can return it.
Then, you can use the return inside the if : it will stop the function and return the value just right you find what you are loonking for.
The === is the strict comparaison operator, checking var type, allowing you to test this is the exact value you are looking for.
You can add a return with an other value (here -1) if the value is not found at the end of the loop.
function myIndexOf(array, search) {
for(var val in array){
if(array[val] === search){
return val;
}
}
return -1;
}
var myArray = ['a', 'b', 'c'];
console.log(myIndexOf(myArray, 'c')); //2
console.log(myIndexOf(myArray, 'f')); //-1 <- Not found
This would work:
var index = 0;
var target = 'c';
var collection = ['a', 'b', 'c', 'd'];
function findIndex(){
for(var val in collection) {
if(collection[val] === target){
return index;
}
index++;
}
}
findIndex();
You can use every as well. Iterate through the array, return true if value doesn't match value being searched so every continues, otherwise set ind and return false.
var getIndex = function(arr, match){
var ind = -1;
arr.every(function(val, index) {
if (val === match){
ind = index;
return false;
}
return true;
});
return ind;
}
getIndex([1, 2, 3], 2);

JavaScript: How to match out-of-order arrays

I'm trying to work out how to match arrays that share the same elements, but not necessarily in the same order.
For example, these two arrays share the same set of elements, even though they're in a different order.
Is there any way to determine whether two arrays contain the same elements?
var search1 = ["barry", "beth", "debbie"];
var search2 = ["beth", "barry", "debbie"];
if (search1 == search2) {
document.write("We've found a match!");
} else {
document.write("Nothing matches");
}
I've got a Codepen of this running at the moment over here: http://codepen.io/realph/pen/grblI
The problem with some of the other solutions is that they are of O(n²) complexity, if they're using a for loop inside of a for loop. That's slow! You don't need to sort either—also slow.
We can speed this up to O(2n) complexity1 by using a simple dictionary. This adds O(2n) storage, but that hardly matters.
JavaScript
var isEqual = function (arr1, arr2) {
if (arr1.length !== arr2.length) {
return false; // no point in wasting time if they are of different lengths
} else {
var holder = {}, i = 0, l = arr2.length;
// holder is our dictionary
arr1.forEach(function (d) {
holder[d] = true; // put each item in arr1 into the dictionary
})
for (; i < l; i++) { // run through the second array
if (!(arr2[i] in holder)) return false;
// if it's not in the dictionary, return false
}
return true; // otherwise, return true
}
}
Test Case
var arr1 = ["barry", "beth", "debbie"],
arr2 = ["beth", "barry", "debbie"];
console.log(isEqual(arr1,arr2));
// returns true
fiddle
Improvement
As Ahruss pointed out, the above function will return true for two arrays that are seemingly equal. For example, [1,1,2,3] and [1,2,2,3] would return true. To overcome this, simply use a counter in the dictionary. This works because !undefined and !0 both return true.
var isReallyEqual = function (arr1, arr2) {
if (arr1.length !== arr2.length) {
return false; // no point in wasting time if they are of different lengths
} else {
var holder = {}, i = 0, l = arr2.length;
// holder is our dictionary
arr1.forEach(function (d) {
holder[d] = (holder[d] || 0) + 1;
// checks whether holder[d] is in the dictionary: holder[d] || 0
// this basically forces a cast to 0 if holder[d] === undefined
// then increments the value
})
for (; i < l; i++) { // run through the second array
if (!holder[arr2[i]]) { // if it's not "in" the dictionary
return false; // return false
// this works because holder[arr2[i]] can be either
// undefined or 0 (or a number > 0)
// if it's not there at all, this will correctly return false
// if it's 0 and there should be another one
// (first array has the element twice, second array has it once)
// it will also return false
} else {
holder[arr2[i]] -= 1; // otherwise decrement the counter
}
}
return true;
// all good, so return true
}
}
Test Case
var arr1 = [1, 1, 2],
arr2 = [1, 2, 2];
isEqual(arr1, arr2); // returns true
isReallyEqual(arr1, arr2); // returns false;
1: It's really O(n+m) complexity, whereby n is the size of the first array and m of the second array. However, in theory, m === n, if the arrays are equal, or the difference is nominal as n -> ∞, so it can be said to be of O(2n) complexity. If you're feeling really pedantic, you can say it's of O(n), or linear, complexity.
you can use this function to compare two arrays
function getMatch(a, b) {
for ( var i = 0; i < a.length; i++ ) {
for ( var e = 0; e < b.length; e++ ) {
if ( a[i] === b[e] ){
return true;
}
}
}
}
Feed your arrays to the following function:
function isArrayEqual(firstArray, secondArray) {
if (firstArray === secondArray) return true;
if (firstArray == null || secondArray == null) return false;
if (firstArray.length != secondArray.length) return false;
// optional - sort the arrays
// firstArray.sort();
// secondArray.sort();
for (var i = 0; i < firstArray.length; ++i) {
if (firstArray[i] !== secondArray[i]) return false;
}
return true;
}
Now you may be thinking, can't I just say arrayOne.sort() and arrayTwo.sort() then compare if arrayOne == arrayTwo? The answer is no you can't in your case. While their contents may be the same, they're not the same object (comparison by reference).
You need to simply sort them, then compare them
function compareArrayItems(array1, array2){
array1 = array1.sort();
array2 = array2.sort();
return array1.equals(array2);
}
fiddle
You can use the equals function provided in How to compare arrays in JavaScript?
Sort them firstly. Secondly, if their length is different, then they're not a match.
After that, iterate one array and test a[i] with b[i], a being the first array, b the second.
var search1 = ["barry", "beth", "debbie"],
search2 = ["beth", "barry", "debbie"];
// If length are different, than we have no match.
if ((search1.length != search2.length) || (search1 == null || search2 == null))
document.write("Nothing matches");
var a = search1.sort(),
b = search2.sort(),
areEqual = true;
for (var i = 0; i < a.length; i++) {
// if any two values from the two arrays are different, than we have no match.
if (a[i] != b[i]) {
areEqual = false;
break; // no need to continue
}
}
document.write(areEqual ? "We've found a match!" : "Nothing matches");

Javascript/Jquery array searching

I'm working on an application that uses nested arrays in Javascript. I have an array like this:
var a = ["Once upon a time there was a man."];
a[1] = ["He was tall."];
a[1][1] = ["He often bumped his head."];
a[2] = ["He was short."];
a[2][1] = ["He couldn't reach high shelves."]
Is there an easy way to figure out what level of an array a value is at? I'd like to be able to input "He often bumped his head" and have it return "a[1][1]" (or if I input "He was short" I'd get back "a[2]"). The arrays will be of indeterminate and fluctuating size.
Thanks in advance for any help with this. I'm new to javascript and jquery, so any explanation of your solution will be highly appreciated. Thanks again.
For a two level array, you could do it like this:
function findMultiArray(array, str) {
var innerArray;
for (var i = 0; i < array.length; i++) {
innerArray = array[i];
for (var j = 0; j < innerArray.length; j++) {
if (innerArray[j] == str) {
return([i, j]);
}
}
}
return null;
}
Note, I had it just return an actual array with the two indexes since that's a little easier to use if you need the data in data form.
Unlike in your request, it will never return the equivalent of a[1] because a[1] is an array, not a string so the match is with a[1][0], not with a[1]. In that case, it would return [1,0].
If you want the return value in a different form, there's just one line of code where is sees the match and i and j are the indexes that matched.
If the level of arrays can be arbitrarily deep with different depths in different places, then that would be a little more involved and would require interrogating the type of items to see if they contained nested arrays or just strings and would require some sort of stack to maintain the current position. It would be easiest to do with recursion.
Some recursion should do the trick:
Array.prototype.recursiveIndexOf = function(item, start) {
var i = this.indexOf(item);
var r, c;
start = start || [];
if(i > -1) {
return start.concat([i]);
}
for(i = 0; i < this.length; i++) {
c = this[i];
if(Object.prototype.toString.call(c) === '[object Array]') {
r = c.recursiveIndexOf(item, start.concat(i));
if(r !== null) {
return r;
}
}
}
return null;
};
Here's a demo.
Here is a much simpler answer. ;)
function array_search(ob,str)
{
for(var i=0;i<ob.length;++i)
{
if(typeof(ob[i])=='object'&&(ob[i] instanceof Array))
{
var foo=array_search(ob[i],str);
if(foo!=null)
return i+'->'+foo;
}else
if(typeof(ob[i])=='string'&&ob[i]==str)
{
return i;
}
}
return null;
}
var a = ["Once upon a time there was a man."];
a[1] = ["He was tall."];
a[1][1] = ["He often bumped his head."];
a[2] = ["He was short."];
a[2][1] = ["He couldn't reach high shelves."]
alert(array_search(a,'He was short.'));

Categories

Resources