375

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?

6
  • 1
    Why do you want to sort the list first before searching for the property? Commented Aug 24, 2011 at 14:24
  • JavaScript objects are {Key:Value}, I fixed it for you. Commented Aug 24, 2011 at 14:30
  • possible duplicate of Search JSON array for matching attribute Commented Jan 19, 2012 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. Commented Jun 25, 2019 at 19:04
  • Related question: stackoverflow.com/questions/10557486/… Commented May 25, 2020 at 20:21

22 Answers 22

484

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');
Sign up to request clarification or add additional context in comments.

12 Comments

Perfect! How's the efficiency of that method compared to the ones explained above?
I have to iterate the entire array no matter what. The first method don't need to.
Simple, but take into account the performance when using large datasets
Does ES2015 give us a way to dynamically search by field name, e.g.
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
|
415

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');

6 Comments

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 didn't you mean NOT greedy? Greedy means more results and findIndex returns only the first match.
Sorry @Jos 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 ;)
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! :-)
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.
|
194

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().

3 Comments

Added answer extending this to cover searching deeper than one attribute level. Thanks for this Chris - really helped :D
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.
DON'T DO THIS. Learn how to use higher-order functions this is the old way of doing things.
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");

1 Comment

The answer below provides more information and was posted first.
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.

4 Comments

Use "===" for string comparison it's the only thing missing in this answer.
@BrunoTavares, which scenario do you think would be different in this case?
@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.
@edank take a look in this answer stackoverflow.com/questions/359494/…
4

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.

Comments

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.

1 Comment

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.
2

you can use filter method

 const filteredData = data.filter(e => e.name !== 'john');

Comments

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);

4 Comments

This should be if(Data[item].name == 'John'), I fixed it for you.
This have one moment. If 'not found' variable i contain positive index. And to test 'not found' needs to compare if i===Data.length
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]);
But isn't there a way without an explicit loop?
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;
});

Comments

1
collection.findIndex(item => item.value === 'smth') !== -1

2 Comments

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!
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).
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.

Comments

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.

Comments

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.

1 Comment

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).
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.

Comments

1

Use this:

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

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

Comments

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.

2 Comments

Add some explanation with answer for how this answer help OP in fixing current issue
@ρяσѕρєяK, Thanks for your suggestion, i already add the explanation :)
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.

Comments

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

Comments

1

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

1 Comment

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.
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);

Comments

-4

Using Underscore.js:

var index = _.indexOf(_.pluck(item , 'name'), 'Nick');

Comments

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.