How can I get the index of an object by its property in JavaScript?
Asked Answered
H

22

371

For example, I have:

var Data = [
  { id_list: 1, name: 'Nick', token: '312312' },
  { id_list: 2, name: 'John', token: '123123' },
]

Then, I want to sort/reverse this object by name, for example. And then I want to get something like this:

var Data = [
  { id_list: 2, name: 'John', token: '123123' },
  { id_list: 1, name: 'Nick', token: '312312' },
]

And now I want to know the index of the object with property name='John' to get the value of the property token.

How do I solve the problem?

Hardihood answered 24/8, 2011 at 14:13 Comment(6)
Why do you want to sort the list first before searching for the property?Anesthetize
JavaScript objects are {Key:Value}, I fixed it for you.Syndicalism
possible duplicate of Search JSON array for matching attributeJules
If you scan through the answers, it appears like there is some native Data object. It is merely a capitalized variable name, which is against convention. If anyone else is bothered by this, I will make edits to the question and answers to fix this naming.Arkansas
Related question: #10557986Pharyngology
Efficiency is not good because we are mapping through the entire array and making a new array just to find this index.Zonate
P
195

As the other answers suggest, looping through the array is probably the best way. But I would put it in its own function, and make it a little more abstract:

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

var Data = [
    {id_list: 2, name: 'John', token: '123123'},
    {id_list: 1, name: 'Nick', token: '312312'}
];

With this, not only can you find which one contains 'John', but you can find which contains the token '312312':

findWithAttr(Data, 'name', 'John'); // returns 0
findWithAttr(Data, 'token', '312312'); // returns 1
findWithAttr(Data, 'id_list', '10'); // returns -1

The function returns -1 when not found, so it follows the same construct as Array.prototype.indexOf().

Peashooter answered 24/8, 2011 at 15:45 Comment(3)
Added answer extending this to cover searching deeper than one attribute level. Thanks for this Chris - really helped :DBothnia
To those who will be having problem with this solution, remember that the === equal sign will not only check the value but also the datatype. so comparing date, numbers and empty values might return false if the actual value is a string. You can use == equal sign if the datatype is of no essence for you.Synonymize
DON'T DO THIS. Learn how to use higher-order functions this is the old way of doing things.Boutte
L
477

Since the sort part is already answered. I'm just going to propose another elegant way to get the indexOf of a property in your array

Your example is:

var Data = [
    {id_list:1, name:'Nick', token:'312312'},
    {id_list:2, name:'John', token:'123123'}
]

You can do:

var index = Data.map(function(e) { return e.name; }).indexOf('Nick');

var Data = [{
    id_list: 1,
    name: 'Nick',
    token: '312312'
  },
  {
    id_list: 2,
    name: 'John',
    token: '123123'
  }
]
var index = Data.map(function(e) {
  return e.name;
}).indexOf('Nick');
console.log(index)

Array.prototype.map is not available on Internet Explorer 7 or Internet Explorer 8. ES5 Compatibility

And here it is with ES6 and arrow syntax, which is even simpler:

const index = Data.map(e => e.name).indexOf('Nick');
Legging answered 4/4, 2014 at 13:54 Comment(12)
Perfect! How's the efficiency of that method compared to the ones explained above?Tun
I have to iterate the entire array no matter what. The first method don't need to.Legging
Simple, but take into account the performance when using large datasetsNievesniflheim
Does ES2015 give us a way to dynamically search by field name, e.g.Messroom
This answer probably represents the best compromise of ease of use vs browser compatibility (see caniuse.com/#search=map), all current browsers support it and there are polyfills readily available for older browsers (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… for example).Chantey
Doesn't this loop over an array twice. It first loops over the original array to create a new array from map and then it loops over the new array to get the indexOf Nick.Sarene
So you create an array just to search? Seems highly inefficient.Coveney
arrays in javascript are objectLegging
Doing a map and then indexOf in the ES6 version does mean they'll be looping over the array twice. Instead you should use the findIndex method shown in my answerSarene
If it's sorted couldn't you perform a binary search of the array instead of just looping through it?Lossa
+1 for readability. -2 for performance compared to the accepted answer.Chaqueta
silverlight513's answer is FAR more efficient than this. This answer loops the ENTIRE array, creates a NEW array, and then iterates that new array until it finds the match. Silverlight's answer simply iterates the array until it finds the match and then it breaks. This answer should not be accepted.Zonate
S
405

If you're fine with using ES6, arrays now have the findIndex function. Which means you can do something like this:

const index = Data.findIndex(item => item.name === 'John');
Sarene answered 16/9, 2016 at 10:19 Comment(6)
hands down the most direct and elegant solution, I'd make a small not that findIndex is greedy - in my case that was perfect - but users be aware that if you have objects with duplicate values (i.e. two objects with name: John) this will return only the first matchBorsch
@Borsch didn't you mean NOT greedy? Greedy means more results and findIndex returns only the first match.Chaqueta
Sorry @Chaqueta but I think you don't quite understand what a greedy search algorithm does. A greedy algorithm considers only the information it has at hand, which is computationally efficient for, say, searching for an item in a list when you need only one result. The findIndex method is greedy preciely because it returns only the first match. It if wasn't greedy it would continue searching. You are thinking of the term "greedy" as it's used in day-to-day English, but in the context of programming (i.e. SO), it's the opposite of what you think ;)Borsch
Thanks for explaining @GrayedFox. My reference to greedy was actually from programming too but related to regular expressions where greedy takes more characters than non-greedy! :-)Chaqueta
Perfect. Exactly what I was looking for. Thank you.Herwig
THIS is better than the answers above as it will break the loop upon first find rather than iterate the entire array and make a new array, ONLY to then perform the excise that is performed in this solution AFTER looping the entire array.Zonate
P
195

As the other answers suggest, looping through the array is probably the best way. But I would put it in its own function, and make it a little more abstract:

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

var Data = [
    {id_list: 2, name: 'John', token: '123123'},
    {id_list: 1, name: 'Nick', token: '312312'}
];

With this, not only can you find which one contains 'John', but you can find which contains the token '312312':

findWithAttr(Data, 'name', 'John'); // returns 0
findWithAttr(Data, 'token', '312312'); // returns 1
findWithAttr(Data, 'id_list', '10'); // returns -1

The function returns -1 when not found, so it follows the same construct as Array.prototype.indexOf().

Peashooter answered 24/8, 2011 at 15:45 Comment(3)
Added answer extending this to cover searching deeper than one attribute level. Thanks for this Chris - really helped :DBothnia
To those who will be having problem with this solution, remember that the === equal sign will not only check the value but also the datatype. so comparing date, numbers and empty values might return false if the actual value is a string. You can use == equal sign if the datatype is of no essence for you.Synonymize
DON'T DO THIS. Learn how to use higher-order functions this is the old way of doing things.Boutte
I
40

If you're having issues with Internet Explorer, you could use the map() function which is supported from 9.0 onward:

var index = Data.map(item => item.name).indexOf("Nick");
Indeed answered 3/1, 2019 at 1:33 Comment(1)
The answer below provides more information and was posted first.Casals
V
33
var index = Data.findIndex(item => item.name == "John")

Which is a simplified version of:

var index = Data.findIndex(function(item){ return item.name == "John"})

From mozilla.org:

The findIndex() method returns the index of the first element in the array that satisfies the provided testing function. Otherwise -1 is returned.

Vuong answered 19/3, 2018 at 19:29 Comment(4)
Use "===" for string comparison it's the only thing missing in this answer.Dislodge
@BrunoTavares, which scenario do you think would be different in this case?Vuong
@Vuong If OP were to check the token field instead of the name field there might be problems, as Data[0].token == 312312 evaluates to true, but Data[0].token === 321321 evaluates to false.Subterrane
@Vuong take a look in this answer #359994Dislodge
O
5

Only way known to me is to loop through all array:

var index = -1;
for(var i=0; i<Data.length; i++)
  if(Data[i].name === "John") {
    index = i;
    break;
  }

Or case insensitive:

var index = -1;
for(var i=0; i<Data.length; i++)
  if(Data[i].name.toLowerCase() === "john") {
    index = i;
    break;
  }

On result variable index contain index of object or -1 if not found.

Osmund answered 24/8, 2011 at 14:23 Comment(0)
H
2

A prototypical way

(function(){
  if (!Array.prototype.indexOfPropertyValue){
       Array.prototype.indexOfPropertyValue = function(prop, value){
      for (var index = 0; index < this.length; index++){
        if (this[index][prop]){
          if (this[index][prop] == value){
            return index;
          }
        }
       }
      return -1;
    }
  }
 })();

 // Usage:
 var Data = [
   {id_list:1, name:'Nick', token:'312312'}, {id_list:2, name:'John', token:'123123'}];

 Data.indexOfPropertyValue('name', 'John'); // Returns 1 (index of array);
 Data.indexOfPropertyValue('name', 'Invalid name') // Returns -1 (no result);
 var indexOfArray = Data.indexOfPropertyValue('name', 'John');
 Data[indexOfArray] // Returns the desired object.
Howell answered 24/7, 2013 at 11:22 Comment(1)
I had to change this a little to work for my situation -- I was looking for the index of a property with a value of null and the fifth line was causing this to skip over that index.Forefront
N
2

you can use filter method

 const filteredData = data.filter(e => e.name !== 'john');
Neper answered 22/11, 2021 at 15:32 Comment(0)
D
1

You can use Array.sort using a custom function as a parameter to define your sorting mechanism.

In your example, it would give:

var Data = [
    {id_list:1, name:'Nick',token:'312312'},{id_list:2,name:'John',token:'123123'}
]

Data.sort(function(a, b){
    return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
});

alert("First name is : " + Data[0].name); // alerts 'John'
alert("Second name is : " + Data[1].name); // alerts 'Nick'

The sort function must return either -1 if a should come before b, 1 if a should come after b and 0 if both are equal. It's up to you to define the right logic in your sorting function to sort the array.

Missed the last part of your question where you want to know the index. You would have to loop through the array to find that as others have said.

Deterrent answered 24/8, 2011 at 14:24 Comment(0)
F
1

Just go through your array and find the position:

var i = 0;
for(var item in Data) {
    if(Data[item].name == 'John')
        break;
    i++;
}
alert(i);
Filter answered 24/8, 2011 at 14:24 Comment(4)
This should be if(Data[item].name == 'John'), I fixed it for you.Syndicalism
This have one moment. If 'not found' variable i contain positive index. And to test 'not found' needs to compare if i===Data.lengthOsmund
Second moment is if array contains nonindexer custom properties. For example: var Data[1,2,3,4,5]; Data["test"]=7897; Compare output of: for(var i in Data)alert(Data[i]); and for(var i=0;i<Data.length;i++)alert(Data[i]);Osmund
But isn't there a way without an explicit loop?Blastoderm
A
1

This might be useful:

function showProps(obj, objName) {
  var result = "";
  for (var i in obj)
    result += objName + "." + i + " = " + obj[i] + "\n";
  return result;
}

I copied this from Working with objects.

Ahmad answered 4/2, 2012 at 8:46 Comment(0)
L
1

Use a small workaround:

Create a new array with names as indexes. After that all searches will use indexes. So, only one loop. After that you don't need to loop through all elements!

var Data = [
    {id_list:1, name:'Nick',token:'312312'},{id_list:2,name:'John',token:'123123'}
    ]
var searchArr = []
Data.forEach(function(one){
  searchArr[one.name]=one;
})
console.log(searchArr['Nick'])

http://jsbin.com/xibala/1/edit

Live example.

Lengthy answered 24/1, 2015 at 9:1 Comment(1)
That means looping trough everything first... then modifying objects (unless you clone them but that is even more overhead). And after that you loop again (partially at least).Chaqueta
B
1

I extended Chris Pickett's answer, because in my case I needed to search deeper than one attribute level:

function findWithAttr(array, attr, value) {
  if (attr.indexOf('.') >= 0) {
    var split = attr.split('.');
    var attr1 = split[0];
    var attr2 = split[1];
    for(var i = 0; i < array.length; i += 1) {
      if(array[i][attr1][attr2] === value) {
        return i;
      }
    }
  } else {
    for(var i = 0; i < array.length; i += 1) {
      if(array[i][attr] === value) {
        return i;
      }
    }
  };
};

You can pass 'attr1.attr2' into the function.

Bothnia answered 30/11, 2015 at 17:38 Comment(0)
P
1

Use this:

Data.indexOf(_.find(Data, function(element) {
  return element.name === 'John';
}));

It is assuming you are using Lodash or Underscore.js.

Prepositive answered 18/1, 2017 at 11:46 Comment(0)
D
1
var fields = {
  teste:
  {
    Acess:
    {
      Edit: true,
      View: false
    }
  },
  teste1:
  {
    Acess:
    {
      Edit: false,
      View: false
    }
  }
};

console.log(find(fields,'teste'));

function find(fields,field) {
  for(key in fields) {
    if(key == field) {
      return true;
    }
  }
  return false;
}

If you have one Object with multiple objects inside, if you want know if some object are include on Master object, just use find(MasterObject, 'Object to Search'). This function will return the response if it exists or not (TRUE or FALSE). I hope to help with this - can see the example on JSFiddle.

Derange answered 18/7, 2017 at 14:40 Comment(2)
Add some explanation with answer for how this answer help OP in fixing current issueFixer
@ρяσѕρєяK, Thanks for your suggestion, i already add the explanation :)Derange
H
1
let indexOf = -1;
let theProperty = "value"
let searchFor = "something";

theArray.every(function (element, index) {

    if (element[theProperty] === searchFor) {
        indexOf = index;
        return false;
    }
    return true;
});
Helfant answered 4/9, 2017 at 16:54 Comment(0)
G
1
collection.findIndex(item => item.value === 'smth') !== -1
Grenadines answered 26/1, 2018 at 16:8 Comment(2)
While this code snippet may solve the question, including an explanation really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. Please also try not to crowd your code with explanatory comments, as this reduces the readability of both the code and the explanations!Cystocarp
An explanation would be in order. You can edit your answer (but without "Edit:", "Update:", or similar - the answer should appear as if it was written today).Blastoderm
S
1

If you want to get the value of the property token then you can also try this:

    let data=[
      { id_list: 1, name: 'Nick', token: '312312' },
      { id_list: 2, name: 'John', token: '123123' },
    ]

    let resultingToken =  data[_.findKey(data,['name','John'])].token

where _.findKey is a Lodash function.

Softcover answered 14/11, 2019 at 12:0 Comment(0)
G
1

You can use findIndex in Lodash library.

Example:

var users = [
{ 'user': 'barney',  'active': false },
{ 'user': 'fred',    'active': false },
{ 'user': 'pebbles', 'active': true }
            ];

_.findIndex(users, function(o) { return o.user == 'barney'; });
// => 0

// The `_.matches` iteratee shorthand.
_.findIndex(users, { 'user': 'fred', 'active': false });
// => 1

// The `_.matchesProperty` iteratee shorthand.
_.findIndex(users, ['active', false]);
// => 0

// The `_.property` iteratee shorthand.
_.findIndex(users, 'active');
// => 2
Gatto answered 29/8, 2021 at 12:22 Comment(0)
W
0

Alternatively to German Attanasio Ruiz's answer, you can eliminate the second loop by using Array.reduce() instead of Array.map();

var Data = [
    { name: 'hypno7oad' }
]
var indexOfTarget = Data.reduce(function (indexOfTarget, element, currentIndex) {
    return (element.name === 'hypno7oad') ? currentIndex : indexOfTarget;
}, -1);
Wilmerwilmette answered 4/11, 2014 at 20:45 Comment(0)
B
0

Maybe the Object.keys, Object.entries, and Object.values methods might help.

Blarney answered 5/4, 2022 at 1:44 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Monsoon
L
-4

Using Underscore.js:

var index = _.indexOf(_.pluck(item , 'name'), 'Nick');
Limerick answered 10/3, 2020 at 6:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.