1

I need to find a missing array in an "array of arrays". I started by finding this function below (on StackOverflow):

function findDeselectedItem(CurrentArray, PreviousArray) {

   var CurrentArrSize = CurrentArray.length;
   var PreviousArrSize = PreviousArray.length;
   var deselectedItem = [];

   // loop through previous array
   for(var j = 0; j < PreviousArrSize; j++) {

      // look for same thing in new array
      if (CurrentArray.indexOf(PreviousArray[j]) == -1)
         deselectedItem.push(PreviousArray[j]);

   }

   return deselectedItem;
}

This works just fine if you did something like this:

oldarray = ["hi", "ho", "hey"];
newarray = ["hi", "hey"];

Using findDeselectedItem(newarray, oldarray) would return ["ho"].

However, my content looks like this:

oldarray = [["James", 17, 1], ["Olivia", 16, 0], ["Liam", 18, 1]];
newarray = [["Olivia", 16, 0], ["James", 17, 1]];

How can I adapt the function above so that it returns the missing array containing 'Liam'.

Thanks

1
  • In case of checking array of arrays indexOf will return a positive or zero value only if references of arrays are same. It wont do a comparison by value Commented Jul 27, 2013 at 12:05

6 Answers 6

2

I would make a hash with the name as a key. That would make finding missing content trivial and very fast. You can then optimize the method by not rebuilding the hash every time, but only when it's really necessary.

var oldArray = [["James", 17, 1], ["Olivia", 16, 0], ["Liam", 18, 1]];
var newArray = [["Olivia", 16, 0], ["James", 17, 1]];

function findDeselectedItems(oldArray, newArray)
{
   var results = [];

   var hash = {};
   for (var i=0; i<newArray.length; i++) {          
      hash[newArray[i].join(',')] = true;
   }

   for (var i=0; i<oldArray.length; i++) {
      if (!hash[oldArray[i].join(',')]) {
        results.push(oldArray[i]);
      }   
   }

   return results;
}   
Sign up to request clarification or add additional context in comments.

9 Comments

be careful, (until my edit gets accepted) the current algorithm only checks the first element of each array. so ['bob', 1, 1] will be 'found' in the array of arrays; [['bob', 2, 2]]
@Hashbrown To overcome dependency on name same algo can be optimized - hash[newArray[i].toString()] = true; and comparisons would become if (hash[oldArray[i].toString()] == null)
Ok, edit accepted. But I don't see how !has[key] is any better than hash[key] == null. I would prefer an obvious comparison and not implicit conversion of null to a Boolean value.
thanks, ==null was an implicit conversion from undefined. Remember boolean ! || && operators do not convert anything, exemplified by (1 || 2) == 1, not true. !<value> is a valid way of doing things without implicities
@parsun, true, that was what I proposed (except I forgot toString() inserted commas, I didnt want ['bob', 1, 1] to hash as 'bob11')
|
1

The problem may be that indexOf uses strict equality. I.e. if an item in the 'previous' array isn't literally also in the 'current' array, it will report it to not be in there.

You will have to iterate over the values yourself (instead of using indexOf) and check if the array contains something that is 'the same as' (but not literally the same) the array.

I.e. if I didn't explain myself well enough take a look at this;

['bob'] == ['bob']; //false
//therefore
[['bob']].indexOf(['bob']); //-1

3 Comments

I upvoted @NoxNoctis' answer. This 'answer' is more a reason behind it. His answer rectified your problem by evaluating the arrays as strings and comparing them
Thanks for your answer, you did teach me something new about indexOf :-)
send me an upvote then :) SO had a good system in place for all kinds of answers :P
0

I hope that this helps you,

function findDeselectedItem(CurrentArray, PreviousArray) {

    var CurrentArrSize = CurrentArray.length;
    var PreviousArrSize = PreviousArray.length;
    var deselectedItem = [];

    // loop through previous array
    for(var j = 0; j < PreviousArrSize; j++) {
        var checkArray = PreviousArrSize[j];
        // loop through 2nd array to match both array

        for(var i = 0; i < CurrentArrSize; i++) {
            // look for same thing in new array
            if (CurrentArray[i].indexOf(checkArray) == -1)
                deselectedItem.push(CurrentArray[i]);

        }
    }
    return deselectedItem;
}

1 Comment

Hi, I just tried this in console and it didn't return the missing one.
0

@KarelG: nice and quick solution but should it not be var checkArray = PreviousArr[j]; instead of var checkArray = PreviousArrSize[j]; ?

3 Comments

Welcome to StackOverflow, this answer should be a comment, however. Also, KarelG's solution didn't work, but your solution didn't fix it either.
Hi Joel, I would have loved to put my remark in a comment, but the settings (I am a novice here and don't have a "reputation">50) unfortunately don't allow it. :-/ So far I can only comment on my own posts :-(
Oh my apologies, sounds like something SO should look into. Thanks anyway :)
0
function findDeselectedItem(CurrentArray, PreviousArray) {

    var CurrentArrSize = CurrentArray.length;
    var PreviousArrSize = PreviousArray.length;
    var deselectedItem = [];
    var selectedIndices = [];

  // loop through previous array
  for(var j = 0; j < PreviousArrSize; j++) {

   for(k=0; k < CurrentArrSize ; k++){
      if (CurrentArray[k].toString() === PreviousArray[j].toString()){
          selectedIndices.push(j);
          break;
      }

   }

 }

    for(var l = 0; l < PreviousArrSize; l++){
      if(selectedIndices.indexOf(l) === -1){
         deselectedItem.push(PreviousArray[l]);
      }
    }

      return deselectedItem;
}

Comments

0

I don't think you can use indexOf to compare two arrays. You need a deeper comparison. Although this code could be written another way, you could do this with an array comparison function and using Array.some() to filter through your elements. Here's an example and a fiddle;

// Credit http://stackoverflow.com/questions/7837456/comparing-two-arrays-in-javascript
// attach the .compare method to Array's prototype to call it on any array
Array.prototype.compare = function (array) {
    // if the other array is a falsy value, return
    if (!array)
        return false;

    // compare lengths - can save a lot of time
    if (this.length != array.length)
        return false;

    for (var i = 0; i < this.length; i++) {
        // Check if we have nested arrays
        if (this[i] instanceof Array && array[i] instanceof Array) {
            // recurse into the nested arrays
            if (!this[i].compare(array[i]))
                return false;
        }
        else if (this[i] != array[i]) {
            // Warning - two different object instances will never be equal: {x:20} != {x:20}
            return false;
        }
    }
    return true;
}

function findDeselectedItem(CurrentArray, PreviousArray) {

    var CurrentArrSize = CurrentArray.length;
    var PreviousArrSize = PreviousArray.length;
    var deselectedItem = [];

    // loop through previous array
    for (var j = 0; j < PreviousArrSize; j++) {
        // look for same thing in new array
        CurrentArray.some(function (a, idx) {
            if(PreviousArray[j].compare(a) == false) {
                deselectedItem.push(PreviousArray[j]);
                return true;
            }
        });
    }

    return deselectedItem;
    }

var oldarray =[["James", 17, 1], ["Olivia", 16, 0], ["Liam", 18, 1]];
var newarray =[["Olivia", 16, 0], ["James", 17, 1]];

console.log(findDeselectedItem(newarray, oldarray));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.