1
$.each(constructions, function(i,v) { 
if ($.inArray(v.name, map[ii].buildings) == -1) {//stuff}
};

Where constructions is an array of objects, each with a unique name. map[ii].buildings is an array containing some of these objects. I want to iterate each object in constructions, checking if its name parameter appears in the objects of map[ii].buildings.

The above code works if the each element in the map[ii].buildings array is just the text string of the object name, but not if the element is the entire object.. close, but no dice >.<

2
  • Would $.grep() work better for you than $.inArray()? Are you trying to check a unique object ID for buildings, or do you actually have the same object in two different arrays? Commented Oct 14, 2014 at 23:32
  • @nothingisnecessary originally its the same object, but other values of the object in buildings change, so it would quickly become different, but always with the same name. Commented Oct 14, 2014 at 23:34

2 Answers 2

2

Try using $.grep() instead of $.inArray(); you can specify a function to do the filtering for you.

Instead of checking for -1, you check whether the array that $.grep() returns has length == 0

Simple example: (would be easier if you posted the code / example of what "constructions" objects look like)

    var constructions = [{
        Name: "Mess hall",
        SqFt: 5000
    }, {
        Name: "Infirmary",
        SqFt: 2000
    }, {
        Name: "Bungalow",
        SqFt: 2000
    }, {
        Name: "HQ",
        SqFt: 2000
    }];
    
    var buildings = [{
        Name: "Infirmary",
        SqFt: 2000
    }, {
        Name: "HQ",
        SqFt: 2000
    }];
    
    // found buildings will be list of items in "constructions" that is not in "buildings"
    var foundBuildings = $.grep(constructions, function (constructionsItem) {
        return $.grep(buildings, function (buildingsItem) {
            return buildingsItem.Name === constructionsItem.Name
        }).length == 0; // == 0 means "not in", and > 0 means "in"
    });
    
    // this just renders the results all pretty for ya
    $.each(foundBuildings, function (idx, item) {
        $("#output").append("<div>" + item.Name + "</div>");
    });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='output'></div>

Example jsFiddle: http://jsfiddle.net/eLeuy9eg/3/

Sign up to request clarification or add additional context in comments.

5 Comments

This is looking good... but I should have been more clear, how can I get foundBuildings to actually be the objects in constructions which are NOT in buildings? In context, the game is checking if each building exists in an array, and if they don't, it lists them.
I think you just pass true as the third argument to $.grep(), which will invert the results. See jQuery docs for grep: api.jquery.com/jquery.grep. And you probably want to rewrite the loop construct.
Doesn't seem to be working, the edit i made to your code is here jsfiddle.net/eLeuy9eg/2 - it feels close tho >.<
Updated code and fiddle link, try it now. Basically check length == 0 for items NOT in constructions, or length > 0 for items that ARE in constructions. Just realized should probably change variable name to notFoundBuildings ;)
No problemo, it's just your standard set difference operation to find the set that is the relative complement of buildings in constructions.
0

The non-jQuery way of doing this would be to use filter. Something like this:

// pass in an array and the key for which you want values
// it returns an array of those values
function getValues(arr, key) {
  return arr.map(function (el) { return el[key]; });
}

function notFoundIn(arr, arr2) {

  // grab the names of the buildings
  var buildings = getValues(arr2, 'name');

  // grab the names from the construction objects and filter
  // those that are not in the building array
  return getValues(arr, 'name').filter(function (el) {
    return buildings.indexOf(el) === -1;
  });
}

notFoundIn(constructions, buildings); // eg [ "one", "three" ]

DEMO

You could even add a new method to the array prototype. With this one you can use either simple arrays, or arrays of objects if you pass in a key. Note in this example I've replaced map and filter with loops that perform the same functions, but faster (see comments):

function getValues(arr, key) {
  var out = [];
  for (var i = 0, l = arr.length; i < l; i++) {
    out.push(arr[i][key]);
  }
  return out;
}

if (!Array.prototype.notFoundIn) {
  Array.prototype.notFoundIn = function (inThisArray, key) {
    var thisArr = key ? getValues(this, key) : this;
    var arrIn = key ? getValues(inThisArray, key) : inThisArray;
    var out = [];
    for (var i = 0, l = thisArr.length; i < l; i++) {
      if (arrIn.indexOf(thisArr[i]) === -1) {
        out.push(thisArr[i]);
      }
    }
    return out;
  }
}


constructions.notFoundIn(buildings, 'name');
[1, 2, 3].notFoundIn([2]); // [1, 3]

DEMO

2 Comments

yup, Array.filter() is good stuff. Second example runs OK when no key is passed, but otherwise is a bit slower (jsperf.com/setsubtraction). Why create extra arrays for the object's keys? Since these functions return an array of strings instead of the array of objects the caller would need to then lookup the object from it's key anyway, right?
@nothingisnecessary Thanks for the reply. That's interesting. If I eliminate filter and map and replace them with simple loops, the prototype replacement method mentioned above comes out faster than grep.

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.