1

I'm pulling my hair out over this one. I have two arrays, likes & dislikes, both filled with about 50 strings each.

I also have a JSON object, data.results, which contains about 50 objects, each with an _id parameter.

I'm trying to check find all the objects within data.results that aren't in both likes and dislikes.

Here's my code at present:

var newResults = []
      for(var i = 0; i<data.results.length; i++){
        for(var x = 0; x<likes.length; x++){
          if(!(data.results[i]._id == likes[x])){
            for(var y = 0; y<dislikes.length; y++){
                if(!(data.results[i]._id == dislikes[y])){
                  newResults.push(data.results[i]);
                  console.log("pushed " + data.results[i]._id);
                }
                else
                {
                   console.log("They already HATE " + data.results[i]._id + " foo!"); //temp
                }
            }
          }
          else
          {
             console.log(data.results[i]._id + " is already liked!"); //temp
          }
        }
      }

As you can see, I'm iterating through all the data.results objects. Then I check whether their _id is in likes. If it isn't, I check whether it's in dislikes. Then if it still isn't, I push it to newResults.

As you might expect by looking at it, this code currently pushes the result into my array once for each iteration, so i end up with a massive array of like 600 objects.

What's the good, simple way to achieve this?

JVG
  • 20,198
  • 47
  • 132
  • 210
  • 1
    @rps: This is probably *[not a good idea](http://stackoverflow.com/questions/500504/why-is-using-for-in-with-array-iteration-such-a-bad-idea)*. – Scott Sauyet Jul 30 '13 at 11:29
  • @ScottSauyet thanks , never thought about the `Array.prototype.foo = 1;` but I have always used it so far without problems, Guess I better stop now. –  Jul 30 '13 at 11:41
  • @rps btw, using an `for ... in` loop to iterate over an array, is not only a bad idea, but also, way slower than using a standard `for` loop – Moritz Roessler Jul 30 '13 at 11:44
  • @C5H8NNaO4 because it checks whether any value is there or not at each index and returns value alone? –  Jul 30 '13 at 11:46
  • Enumerating the properties of an object includes enumerating properties of its prototype, and the prototype of the prototype, and so on, recursively; (http://es5.github.io/#x12.6.4) In other words, it has to look up the prototype chain of the array, before iterating, which is not the case in a normal for loop – Moritz Roessler Jul 30 '13 at 11:56

4 Answers4

3
for (var i = 0; i < data.results.length; i++) {
    isInLiked = (likes.indexOf(data.results[i]) > -1);
    isInHated = (dislikes.indexOf(data.results[i]) > -1);
    if (!isInLiked && !isInHated) {
        etc...
    }
}
Moos Hueting
  • 650
  • 4
  • 14
  • Note, though, that if you have to support IE < 9, you will probably need a *[shim](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Compatibility)*. Or you could write a simpler *[contains](http://stackoverflow.com/questions/237104/array-containsobj-in-javascript)* function. – Scott Sauyet Jul 30 '13 at 11:26
  • @AndréDion They aren't the same length. – JVG Jul 30 '13 at 11:26
1

When checking whether an Array contains an element, Array.prototype.indexOf (which is ECMAScript 5, but shimmable for older browsers), comes in handy. Even more when combined with the bitwise NOT operator ~ and a cast to a Boolean !

Lets take a look how this could work.

Array.prototype.indexOf returns -1 if an Element is not found.

Applying a ~ to -1 gives us 0, applying an ! to a 0 gives us true.

So !~[...].indexOf (var) gives us a Boolean represantation, of whether an Element is NOT in an Array. The other way round !!~[...].indexOf (var) would yield true if an Element was found.

Let's wrap this logic in a contains function, to simply reuse it.

function contains (array,element) {
    return !!~array.indexOf (element);
}

Now we only need an logical AND && to combine the output, of your 2 arrays, passed to the contains function.

var likes = ["a","b","f"] //your likes
var dislikes = ["c","g","h"] //your dislikes

var result = ["a","c","d","e","f"]; //the result containing the strings
var newresult = []; //the new result you want the strings which are NOT in likes or dislikes, being pushed to

for (var i = 0,j;j=result[i++];) //iterate over the results array
     if (!contains(likes,j) && !contains (dislikes,j)) //check if it is NOT in likes AND NOT in dislikes
           newresult.push (j) //if so, push it to the newresult array.

console.log (newresult) // ["d","e"]

Here is a Fiddle

Edit notes:
1. Added an contains function, as @Scott suggested

Moritz Roessler
  • 8,542
  • 26
  • 51
  • 1
    I might add a `contains` function for readability: `var contains = function(haystack, needle) {return !!~haystack.indexOf(needle);};` and change the test to `if (!contains(likes, j) && !contains(dislikes, j))`. I think that would make the code cleaner, and `contains` is clearly reusable. – Scott Sauyet Jul 30 '13 at 11:43
  • @Scott yep, that sounds like a good idea =), updated the answer – Moritz Roessler Jul 30 '13 at 11:50
0

Use likes.indexOf(data.results[i]._id) and dislikes.indexOf(data.results[i]._id).

if (likes.indexOf(data.results[i]._id) != -1)
{
// they like it :D
}
Valentin D
  • 705
  • 7
  • 14
  • Beware that [`Array.indexOf`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) isn't supported by all browsers. – André Dion Jul 30 '13 at 11:22
  • Right, forgot about old browsers :( here is the implementation for those old browsers. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FArray%2FindexOf#Compatibility – Valentin D Jul 30 '13 at 11:28
0

Try first creating an array of common strings between likes and dislikes

var commonStrAry=[];
for(var i = 0; i<likes.length; i++){
for(var j=0; j<dislikes.length; j++){
    if(likes[i] === dislikes[j]){
        commonStrAry.push(likes[i] );
    }
}
}

then you can use this to check against data.results and just remove the elements that don't match.