113

Given:

var peoples = [
  { "attr1": "bob", "attr2": "pizza" },
  { "attr1": "john", "attr2": "sushi" },
  { "attr1": "larry", "attr2": "hummus" }
];

Wanted:

Index of object where attr === value for example attr1 === "john" or attr2 === "hummus"

Update: Please, read my question carefully, i do not want to find the object via $.inArray nor i want to get the value of a specific object attribute. Please consider this for your answers. Thanks!

Damjan Pavlica
  • 31,277
  • 10
  • 71
  • 76
return1.at
  • 2,992
  • 3
  • 23
  • 26
  • 1
    possible duplicate of [Find object by id in array of javascript objects](http://stackoverflow.com/questions/7364150/find-object-by-id-in-array-of-javascript-objects) – Felix Kling Jun 29 '12 at 08:03
  • 3
    yes, same use case, but i find my question to be more generic eg better hit rate if you do not have a key named "id" you are searching for. – return1.at Jun 29 '12 at 08:14
  • @FelixKling I don't think this is a duplicate because it's trying to get the *index* as opposed to the *object* (the question you point to didn't solve my problem but this one did). – jcuenod Oct 10 '16 at 16:18

8 Answers8

221

The Functional Approach

All the cool kids are doing functional programming (hello React users) these days so I thought I would give the functional solution. In my view it's actually a lot nicer than the imperatival for and each loops that have been proposed thus far and with ES6 syntax it is quite elegant.

Update

There's now a great way of doing this called findIndex which takes a function that return true/false based on whether the array element matches (as always, check for browser compatibility though).

var index = peoples.findIndex(function(person) {
  return person.attr1 == "john"
});

With ES6 syntax you get to write this:

var index = peoples.findIndex(p => p.attr1 == "john");

The (Old) Functional Approach

TL;DR

If you're looking for index where peoples[index].attr1 == "john" use:

var index = peoples.map(function(o) { return o.attr1; }).indexOf("john");

Explanation

Step 1

Use .map() to get an array of values given a particular key:

var values = object_array.map(function(o) { return o.your_key; });

The line above takes you from here:

var peoples = [
  { "attr1": "bob", "attr2": "pizza" },
  { "attr1": "john", "attr2": "sushi" },
  { "attr1": "larry", "attr2": "hummus" }
];

To here:

var values = [ "bob", "john", "larry" ];

Step 2

Now we just use .indexOf() to find the index of the key we want (which is, of course, also the index of the object we're looking for):

var index = values.indexOf(your_value);

Solution

We combine all of the above:

var index = peoples.map(function(o) { return o.attr1; }).indexOf("john");

Or, if you prefer ES6 syntax:

var index = peoples.map((o) => o.attr1).indexOf("john");

Demo:

var peoples = [
  { "attr1": "bob", "attr2": "pizza" },
  { "attr1": "john", "attr2": "sushi" },
  { "attr1": "larry", "attr2": "hummus" }
];

var index = peoples.map(function(o) { return o.attr1; }).indexOf("john");
console.log("index of 'john': " + index);

var index = peoples.map((o) => o.attr1).indexOf("larry");
console.log("index of 'larry': " + index);

var index = peoples.map(function(o) { return o.attr1; }).indexOf("fred");
console.log("index of 'fred': " + index);

var index = peoples.map((o) => o.attr2).indexOf("pizza");
console.log("index of 'pizza' in 'attr2': " + index);
Francisco Campos
  • 1,191
  • 8
  • 16
jcuenod
  • 55,835
  • 14
  • 65
  • 102
  • I'm sad that IE doesn't support `findIndex` function. – wonsuc Oct 23 '18 at 00:20
  • now let's suppose you wanted to know the index of pizza within the line ```{ "attr1": "bob", "attr2": "pizza" }```, that is "1", how would you do that? Or, given the value ```"pizza"``` you wanted to know which is the corresponding ```property name```(i.e.: attr2), given that we are in line 0, how would you do that? – John Galassi Jan 04 '20 at 16:03
  • Maybe it is updated. `findIndex` now returns -1 or found index position. – knoxgon Feb 17 '20 at 10:11
22

If you want to check on the object itself without interfering with the prototype, use hasOwnProperty():

var getIndexIfObjWithOwnAttr = function(array, attr, value) {
    for(var i = 0; i < array.length; i++) {
        if(array[i].hasOwnProperty(attr) && array[i][attr] === value) {
            return i;
        }
    }
    return -1;
}

to also include prototype attributes, use:

var getIndexIfObjWithAttr = function(array, attr, value) {
    for(var i = 0; i < array.length; i++) {
        if(array[i][attr] === value) {
            return i;
        }
    }
    return -1;
}
Community
  • 1
  • 1
return1.at
  • 2,992
  • 3
  • 23
  • 26
  • 1
    +1 , but why not just return i (instead of $.inArray(array[i], array); )? – Me.Name Jun 29 '12 at 08:07
  • 1
    Why do you use `i += 1` instead of `i++`? – Amberlamps Jun 29 '12 at 08:18
  • 1
    Why use ‘array[i].hasOwnProperty(attr)‘ instead of ‘array[i][attr]‘ ? – Alain BECKER Jun 29 '12 at 08:22
  • @AlainSaint-Etienne see http://stackoverflow.com/questions/135448/how-do-i-check-to-see-if-an-object-has-an-attribute-in-javascript – return1.at Jun 29 '12 at 08:27
  • @return1.at Good to know, though (1) a bit overkill here, and most importantly (2) in the general case of your question, we might want to check on the property of the prototype, so ‘array[i][attr]‘ would be a better fit for the general case, and ‘array[i].hasOwnProperty(attr)‘ would be necessary if we specifically want to discard properties defined on a prototype. Did I get it right ? – Alain BECKER Jun 29 '12 at 08:44
  • @AlainSaint-Etienne yes. i did update my answer according to your comments, please check. – return1.at Jun 29 '12 at 09:06
  • 1
    i suggest adding 'return -1' after the 'for' loop, in case the element wasn't found (similar to javascript's 'indexOf' or jquery's '$.inArray') – schellmax Jul 05 '12 at 13:46
  • @AlainSaint-Etienne I agree. There is the problem that {a:undefined} is distinct from {}, but it's not clear what the "right" thing to do in such cases is, and your suggestion is simpler. – podperson Aug 20 '14 at 17:35
8

Using jQuery .each()

var peoples = [
  { "attr1": "bob", "attr2": "pizza" },
  { "attr1": "john", "attr2": "sushi" },
  { "attr1": "larry", "attr2": "hummus" }
];

$.each(peoples, function(index, obj) {
   $.each(obj, function(attr, value) {
      console.log( attr + ' == ' + value );
   });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Using for-loop:

var peoples = [
  { "attr1": "bob", "attr2": "pizza" },
  { "attr1": "john", "attr2": "sushi" },
  { "attr1": "larry", "attr2": "hummus" }
];

for (var i = 0; i < peoples.length; i++) {
  for (var key in peoples[i]) {
    console.log(key + ' == ' + peoples[i][key]);
  }
}
adiga
  • 34,372
  • 9
  • 61
  • 83
thecodeparadox
  • 86,271
  • 21
  • 138
  • 164
6

Not a direct answer to your question, though I thing it's worth mentioning it, because your question seems like fitting in the general case of "getting things by name in a key-value storage".

If you are not tight to the way "peoples" is implemented, a more JavaScript-ish way of getting the right guy might be :

var peoples = {
  "bob":  { "dinner": "pizza" },
  "john": { "dinner": "sushi" },
  "larry" { "dinner": "hummus" }
};

// If people is implemented this way, then
// you can get values from their name, like :
var theGuy = peoples["john"];

// You can event get directly to the values
var thatGuysPrefferedDinner = peoples["john"].dinner;

Hope if this is not the answer you wanted, it might help people interested in that "key/value" question.

Alain BECKER
  • 787
  • 5
  • 15
  • that does not hit my usecase, but maybe is helpful for others. i should update my question for more generic attribute names to make that clear. – return1.at Jun 29 '12 at 08:21
4
function getIndexByAttribute(list, attr, val){
    var result = null;
    $.each(list, function(index, item){
        if(item[attr].toString() == val.toString()){
           result = index;
           return false;     // breaks the $.each() loop
        }
    });
    return result;
}
Alain BECKER
  • 787
  • 5
  • 15
davids
  • 6,259
  • 3
  • 29
  • 50
  • Just to clarify one thing: ‘return false‘ might seem unnecessary. One can do without, but it's a good optimisation as it breaks the ‘$.each()‘ loop. Good job! On the other hand, calling ‘.toString()‘ is weird (why not compare values?), and is likely to cause an error if ‘attr‘ is undefined on the ‘item‘, or if val is ‘null‘... Or did I miss a very good reason to use ‘toString()‘ here? – Alain BECKER Jun 29 '12 at 09:09
  • I don't exactly remember why I've gone that way, but seem to recall it was because of some conflict when the value was a number – davids Jun 29 '12 at 09:32
2

You can also make it a reusable method by expending JavaScript:

Array.prototype.findIndexBy = function(key, value) {
    return this.findIndex(item => item[key] === value)
}

const peoples = [{name: 'john'}]
const cats = [{id: 1, name: 'kitty'}]

peoples.findIndexBy('name', 'john')
cats.findIndexBy('id', 1)

Tycho
  • 131
  • 1
  • 5
1

Do this way:-

var peoples = [
  { "name": "bob", "dinner": "pizza" },
  { "name": "john", "dinner": "sushi" },
  { "name": "larry", "dinner": "hummus" }
];

$.each(peoples, function(i, val) {
    $.each(val, function(key, name) {
        if (name === "john")
            alert(key + " : " + name);
    });
});

OUTPUT:

name : john

Refer LIVE DEMO

Siva Charan
  • 17,940
  • 9
  • 60
  • 95
0

You can use lodash findKey https://docs-lodash.com/v4/find-key/ This works on objects and arrays.

example:

import _findKey from 'lodash.findkey';

    var users = {
      'barney':  { 'age': 36, 'active': true },
      'fred':    { 'age': 40, 'active': false },
      'pebbles': { 'age': 1,  'active': true }
    };
     
    _findKey(users, () => o.age < 40);
    // => 'barney' (iteration order is not guaranteed)
     
    // The `_.matches` iteratee shorthand.
    _findKey(users, { 'age': 1, 'active': true });
    // => 'pebbles'
     
    // The `_.matchesProperty` iteratee shorthand.
    _findKey(users, ['active', false]);
    // => 'fred'
     
    // The `_.property` iteratee shorthand.
    _findKey(users, 'active');
    // => 'barney'
Whitespacecode
  • 1,168
  • 1
  • 5
  • 9