6

I have this array, in which each index contains an object literal. All of the object literals have the same properties. Some of the object literals have the same value for a given property, and I want to create a new array containing only those object literals.

My idea is to sort the array, and slice it into a new array...

Here is the array:

var arr = [];

arr[0] =
{
    country: "United States",
    num: 27
};

arr[1] =
{
    country: "Australia",
    num: 5
};

arr[2] =
{
    country: "United States",
    num: 7
};


So, I want to create a new array containing only those objects where the property country is "United States". This is my crazy idea so far, which doesn't work:

function getNewArray(arr)
{
    var arr2 = [];

    for(var key in arr)
    {
        for(var i = 0; i < arr.length - 1; i++)
        {
            if(arr.hasOwnProperty(key) && arr[i].name == arr[i + 1].name)
            {
                    arr2[i] = arr.slice(key);
            }
        }
    }

    return arr2;
}

var arr3 = getNewArray(arr).sort();
Ian Campbell
  • 2,678
  • 10
  • 56
  • 104
  • Could you please clarify your question: your example with "United States" makes it sound like you want to specify a country and return only the elements with that country, but your initial paragraph seems to be saying the result should include any country that has duplicates. Some answers have gone one way, some the other (and I've just updated my answer to cover both). Maybe you could update your example array to include more than two countries? – nnnnnn May 31 '12 at 06:02
  • I'm sorry, I am actually trying to return an array that contains only matching values. And more recently it turns out that I need such an algorithm that returns multiple arrays from an array, each array containing only the matching values specified.. – Ian Campbell Jun 05 '12 at 21:03

6 Answers6

4

"I want to create a new array containing only those objects where the property country is "United States""

This is exactly what the Array.filter() method is for:

var filteredArray = arr.filter(function(val, i, a) {
                                  return val.country==="United States";
                    });

Note that the .filter() method isn't available in IE before version 9, but the MDN page I linked to above shows you exactly how to implement it so reading that page should in itself answer your question.

Note also that in the (non-working) code in the question, your two for loops are basically doing the same thing as each other because they're both iterating over arr, so it doesn't make sense to nest them like that. You shouldn't use a for..in loop on an array, but if you do the key values will be the numeric indexes, it doesn't somehow pick up the properties of the object stored at each index.

EDIT:

"Some of the object literals have the same value for a given property, and I want to create a new array containing only those object literals."

OK, re-reading this I guess you didn't really want to select elements by specifying a country, you wanted to select elements for any country that had duplicate entries? So if there were another three elements that all had "New Zealand" you'd want to select them in addition to the "United States" ones? If so, you could do something like this:

var countryCount = {},
    i;
for (i = 0; i < arr.length; i++)
    if (countryCount.hasOwnProperty(arr[i].country)
       countryCount[arr[i].country]++;
    else
       countryCount[arr[i].country] = 1;

var filteredArr = arr.filter(function(val, i, a) {
                      return countryCount[val.country] > 1;
                  });
nnnnnn
  • 147,572
  • 30
  • 200
  • 241
  • This is actually more close to what I am trying to do now, which is returning a separate array (from the original) that contains only the specified matching values... and returning multiple separate arrays for each set of matching values, instead of one with all matching values collectively like you are suggesting here. But this is very useful, thanks @nnnnnn. – Ian Campbell Jun 05 '12 at 21:14
2

There is a simpler way of doing this, I think this is what you want var new_arr = arr.filter(function(obj){ return obj['country'] === 'United States'; }) This will filter your results into new_arr Of course you can make it better and more generic than just 'United States'

Edit: Whoops, got your question just now

Answer: Nested Filters :)

function getKeyStuff(key) {
    return arr.filter( function(obj) { 
        var new_arr = arr.filter( function(inner_obj) { 
            return inner_obj[key] === obj[key]; 
        });
        return new_arr.length > 1; 
    });
}
Rishi Diwan
  • 338
  • 1
  • 10
  • This is fantastic, and I would totally use this idea except that my code needs to be compliant with all versions of IE, in which `.filter()` isn't unfortunately (IE sucks!). – Ian Campbell Jun 05 '12 at 21:05
2
var getCountry = function (country) {
    var out = [];
    for (var i = 0, len = arr.length; i < len; i++)
        if (arr[i].country === country) out.push(arr[i]);
    return out;
};

Demo: http://jsfiddle.net/YwytD/1/

elclanrs
  • 92,861
  • 21
  • 134
  • 171
  • This is a nice piece of code, very simple! That's exactly what I need, this got me on the right track.. however, now I am confounded with not knowing exactly what country may or may not be, and how to initialize it from the original array as such. – Ian Campbell Jun 05 '12 at 21:08
  • Where did arr come from ? – Edgar Quintero Nov 15 '18 at 14:33
1

Here is my solution:

// assuming arr is already set

var num_obj = arr.length;
var obj_by_country = {}, obj;
for (var i = 0; i < num_obj; i++) {
  obj = arr[i];
  if (!obj_by_country[obj.country]) {
    obj_by_country[obj.country] = [];
  }
  obj_by_country[obj.country].push(obj);
}

// build final array
var final_array = [];
for (i in obj_by_country) {
  if (obj_by_country[i].length > 1) {
    final_array.push(obj_by_country[i]);
  }
}
ziad-saab
  • 19,139
  • 3
  • 36
  • 31
1

Can you use JQuery?

var arr = [];
arr[0] = { country: "United States", num: 27 };
arr[1] = { country: "Australia", num: 5 };
arr[2] = { country: "United States", num: 7 };

var newArray = [];
$.each(arr, function(){
   if(this.country == "United States")
      newArray.push(this);
});
ltiong_sh
  • 3,186
  • 25
  • 28
  • 1
    If you're going to use jQuery, why not use the [`.grep()` method](http://api.jquery.com/jQuery.grep/)? – nnnnnn May 31 '12 at 05:42
  • Thanks nnnnnn and @Itiong_sh for the jQuery options -- both of these are what I was looking for, and are cross-browser compatible. I may actually end up using such a jQuery algorithm in the end, but the previous answer helped me to work out my logic. – Ian Campbell Jun 05 '12 at 21:11
0
getByKey = function(arr, key, value) {
    var results = [];
    for(var i = 0; i < arr.length; i++) {
       if(arr[i][key] === value) {
           results.push(arr[i]);
       }
    }
    return results
}

here's a working example http://jsfiddle.net/michaelghayes/UyHYz/2/

MiGnH
  • 428
  • 4
  • 11
  • I didn't downvote, but this won't work. I think you meant `if (arr[i][key] === value)`, and you probably intended to push `arr[i]`. Currently your code never uses `i` within the loop... – nnnnnn May 31 '12 at 05:47
  • Thanks, was in a bit of a hurry, fixed it – MiGnH May 31 '12 at 05:55
  • This one is very similar to `elclanrs`'s solution, in that it is simple and effective. However, this is slightly more complex in that there are two extra parameters of `key` and `value` that I don't think I would need in most circumstances. – Ian Campbell Jun 05 '12 at 21:18