355

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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
rsboarder
  • 4,592
  • 3
  • 20
  • 21
  • 1
    Why do you want to sort the list first before searching for the property? – JJJ Aug 24 '11 at 14:24
  • JavaScript objects are `{Key:Value}`, I fixed it for you. – gen_Eric Aug 24 '11 at 14:30
  • possible duplicate of [Search JSON array for matching attribute](http://stackoverflow.com/questions/2166765/search-json-array-for-matching-attribute) – outis Jan 19 '12 at 11:15
  • 1
    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. – Roy Prins Jun 25 '19 at 19:04
  • Related question: https://stackoverflow.com/questions/10557486/in-an-array-of-objects-fastest-way-to-find-the-index-of-an-object-whose-attribu – Anton Tarasenko May 25 '20 at 20:21
  • Efficiency is not good because we are mapping through the entire array and making a new array just to find this index. – wynx May 29 '23 at 23:03

22 Answers22

466

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');
xxx
  • 1,153
  • 1
  • 11
  • 23
German Attanasio
  • 22,217
  • 7
  • 47
  • 63
  • 1
    Perfect! How's the efficiency of that method compared to the ones explained above? – Sheraff May 28 '14 at 14:15
  • 5
    I have to iterate the entire array no matter what. The first method don't need to. – German Attanasio Jun 02 '14 at 02:22
  • 1
    Simple, but take into account the performance when using large datasets – gavioto Nov 16 '15 at 14:21
  • Does ES2015 give us a way to dynamically search by field name, e.g. – brianlmerritt Apr 15 '16 at 12:14
  • This answer probably represents the best compromise of ease of use vs browser compatibility (see http://caniuse.com/#search=map), all current browsers support it and there are polyfills readily available for older browsers (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map#Polyfill for example). – A. Murray Oct 27 '16 at 11:46
  • 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. – silverlight513 Nov 22 '16 at 08:58
  • So you create an array just to search? Seems highly inefficient. – shinzou Apr 06 '17 at 08:16
  • arrays in javascript are object – German Attanasio Apr 06 '17 at 12:06
  • 6
    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 answer – silverlight513 Mar 13 '19 at 11:23
  • If it's sorted couldn't you perform a binary search of the array instead of just looping through it? – Douglas Gaskell Apr 06 '19 at 04:28
  • +1 for readability. -2 for performance compared to the accepted answer. – Jos Sep 12 '19 at 13:45
  • 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. – wynx May 29 '23 at 23:05
384

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');
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
silverlight513
  • 5,268
  • 4
  • 26
  • 38
  • 10
    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 match – GrayedFox Oct 21 '17 at 20:21
  • 1
    @GrayedFox didn't you mean NOT greedy? Greedy means more results and findIndex returns only the first match. – Jos Sep 12 '19 at 13:48
  • 6
    Sorry @Jos but I think you don't quite understand [what a greedy search algorithm does](https://brilliant.org/wiki/greedy-algorithm/). 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 ;) – GrayedFox Sep 13 '19 at 15:52
  • 5
    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! :-) – Jos Sep 17 '19 at 12:57
  • Perfect. Exactly what I was looking for. Thank you. – Darryl Young Mar 05 '20 at 14:11
  • 1
    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. – wynx May 29 '23 at 23:05
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().

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Chris Pickett
  • 2,822
  • 1
  • 14
  • 7
  • 1
    Added answer extending this to cover searching deeper than one attribute level. Thanks for this Chris - really helped :D – Ed Shee Nov 30 '15 at 17:35
  • 4
    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. – David Addoteye Feb 15 '16 at 12:43
  • 4
    DON'T DO THIS. Learn how to use higher-order functions this is the old way of doing things. – arled Feb 01 '20 at 13:39
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");
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Alain T.
  • 40,517
  • 4
  • 31
  • 51
  • 2
    [The answer below](https://stackoverflow.com/a/22864817) provides more information and was posted first. – Alexander Apr 04 '19 at 11:20
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.

edank
  • 599
  • 7
  • 16
  • 2
    Use "===" for string comparison it's the only thing missing in this answer. – Bruno Tavares Jun 27 '18 at 15:23
  • @BrunoTavares, which scenario do you think would be different in this case? – edank Jun 27 '18 at 17:52
  • 1
    @edank 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`. – kem Jun 29 '18 at 19:30
  • @edank take a look in this answer https://stackoverflow.com/questions/359494/which-equals-operator-vs-should-be-used-in-javascript-comparisons – Bruno Tavares Jul 17 '18 at 03:04
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Andrew D.
  • 8,130
  • 3
  • 21
  • 23
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.
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 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. – David Brunow Dec 01 '14 at 03:06
2

you can use filter method

 const filteredData = data.filter(e => e.name !== 'john');
Sakhri Houssem
  • 975
  • 2
  • 16
  • 32
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 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). – Jos Sep 12 '19 at 13:42
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ed Shee
  • 931
  • 1
  • 7
  • 22
1

Use this:

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

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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ellone
  • 3,644
  • 12
  • 40
  • 72
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
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;
});
Itay Merchav
  • 954
  • 8
  • 8
1
collection.findIndex(item => item.value === 'smth') !== -1
Eugene Lyzo
  • 99
  • 1
  • 2
  • While this code snippet may solve the question, [including an explanation](//meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) 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! – Blue Jan 26 '18 at 17:46
  • An explanation would be in order. You can [edit your answer](https://stackoverflow.com/posts/48465127/edit) (but ***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen Jun 11 '21 at 20:04
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Akash Singh
  • 547
  • 5
  • 8
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
Iman Marashi
  • 5,593
  • 38
  • 51
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
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);
Sascha Galley
  • 15,711
  • 5
  • 37
  • 51
  • This should be `if(Data[item].name == 'John')`, I fixed it for you. – gen_Eric Aug 24 '11 at 14:26
  • This have one moment. If 'not found' variable *i* contain positive index. And to test 'not found' needs to compare if i===Data.length – Andrew D. Aug 24 '11 at 14:36
  • 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 – Andrew D. Aug 24 '11 at 14:43
  • But isn't there a way without an explicit loop? – Peter Mortensen Jun 11 '21 at 19:50
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ashish
  • 1,171
  • 11
  • 12
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);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
hypno7oad
  • 1,441
  • 1
  • 19
  • 28
0

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

milah
  • 1
  • 2
  • 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](/help/how-to-answer). – Khushbu Apr 05 '22 at 05:47
-4

Using Underscore.js:

var index = _.indexOf(_.pluck(item , 'name'), 'Nick');
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
M Faisal Hameed
  • 673
  • 1
  • 7
  • 25