如何在JavaScript中通过对象属性获取对象的索引?

362
例如,我有:
var Data = [
  { id_list: 1, name: 'Nick', token: '312312' },
  { id_list: 2, name: 'John', token: '123123' },
]

然后,我想通过name对该对象进行排序/反转,例如。然后我想要获得类似于这样的结果:

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

现在我想知道具有属性 name='John' 的对象的索引,以获取属性标记的值。

我该如何解决这个问题?


1
为什么你想在查找属性之前先对列表进行排序? - JJJ
JavaScript 对象是 {Key:Value},我已经为您修复了它。 - gen_Eric
可能是重复的问题:在JSON数组中搜索匹配属性 - outis
1
如果您浏览答案,似乎有一些本地的“Data”对象。这只是一个大写的变量名,违反了约定。如果其他人也对此感到困扰,我将编辑问题和答案以修复此命名。 - Roy Prins
相关问题:https://dev59.com/hmkv5IYBdhLWcg3wfA4P - Anton Tarasenko
效率不高,因为我们需要遍历整个数组并创建一个新数组才能找到这个索引。 - wynx
22个回答

472

既然排序问题已经解决,我想提出另一种优雅的方法来获取数组中属性的indexOf

你的示例代码如下:

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

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.mapInternet Explorer 7 或 Internet Explorer 8 上不可用。请参考ES5 兼容表

下面是使用 ES6 和箭头语法的代码,甚至更简单:

const index = Data.map(e => e.name).indexOf('Nick');

1
完美!那种方法的效率与上面解释的方法相比如何? - Sheraff
5
无论如何我都必须遍历整个数组。而第一种方法则不需要。 - German Attanasio
1
简单,但在使用大型数据集时要考虑性能。 - gavioto
所以你创建一个数组只是为了搜索?看起来非常低效。 - shinzou
6
在ES6版本中进行地图操作,然后再使用indexOf方法确实意味着它们将会对数组进行两次循环。相反,你应该使用我答案中展示的findIndex方法。 - silverlight513
显示剩余7条评论

394

如果您使用ES6没有问题,数组现在具有findIndex函数。这意味着您可以像这样做:

const index = Data.findIndex(item => item.name === 'John');

10
毫无疑问,这是最直接和优雅的解决方案。我想提醒一下,findIndex方法是贪心的——在我的情况下,这是完美的——但是用户需要知道,如果您有具有重复值的对象(例如,两个具有name: John的对象),它只会返回第一个匹配项。 - GrayedFox
1
@GrayedFox,你是不是想说“非贪婪模式”?贪婪模式意味着返回更多的结果,而findIndex仅返回第一个匹配项。 - Jos
6
抱歉@Jos,我认为你并没有完全理解 贪心算法 的工作原理。贪心算法只考虑手头上的信息,在需要找到一个结果时,这种方法在计算效率方面非常高效,比如在列表中搜索一个项目。findIndex 方法之所以是贪心的,正是因为它只返回第一个匹配项。如果它不是贪心算法,它会继续搜索。你可能把“贪心”这个术语当作日常英语中的用法来思考,但在编程(即SO)的背景下,它与你想象的相反。;) - GrayedFox
5
感谢@GrayedFox解释。我的“贪婪”这个词也是来自编程中的正则表达式,其中“贪婪”比“非贪婪”匹配更多的字符! :-) - Jos
完美。正是我所寻找的。谢谢。 - Darryl Young
1
这种方法比上面的答案更好,因为它在第一次找到时就会打破循环,而不是迭代整个数组并创建一个新数组,然后才执行在此解决方案中在遍历整个数组之后执行的操作。 - wynx

195

正如其他答案所建议的那样,循环遍历数组可能是最好的方法。但我会将其放入一个独立的函数中,并使它更加抽象化:

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'}
];

通过这种方式不仅可以找到包含“John”的内容,还可以找到包含标记“312312”的内容:

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

当未找到时,该函数返回-1,因此它遵循与 Array.prototype.indexOf() 相同的结构。


1
添加了答案,扩展了对多个属性级别进行深入搜索的覆盖范围。感谢您的帮助,Chris - 真的很有用 :D - Ed Shee
4
对于那些可能会遇到此解决方案问题的人,请记住,=== 等号不仅会检查值,还会检查数据类型。 因此,如果实际值是字符串,则比较日期、数字和空值可能会返回 false。 如果数据类型对您没有关键性,可以使用 == 等号。 - David Addoteye
4
不要这样做。学习如何使用高阶函数,这是旧的方法。 - arled

40
如果你在使用Internet Explorer时遇到问题,可以使用从9.0版本开始支持的map()函数:
var index = Data.map(item => item.name).indexOf("Nick");

2
以下回答提供了更多信息,并且是首先发布的。 - Alexander

33
var index = Data.findIndex(item => item.name == "John")

这是一个简化版本:

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

来自mozilla.org:

findIndex() 方法返回数组中满足提供的测试函数的第一个元素的索引。否则返回 -1。


2
在字符串比较时,请使用 "===",这是此答案中唯一缺少的内容。 - Bruno Tavares
@BrunoTavares,你认为在这种情况下会有什么不同的情况吗? - edank
1
如果OP检查token字段而不是name字段,可能会出现问题,因为Data[0].token == 312312评估为true,但Data[0].token === 321321评估为false - kem
@edank,请查看此答案:https://dev59.com/Z3RC5IYBdhLWcg3wROpQ - Bruno Tavares

5

我知道的唯一方法是循环遍历整个数组:

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

或者不区分大小写:

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

在结果变量中,“index”包含对象的索引,如果找不到则为-1。


2

一个原型化的方法

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

我不得不稍微修改一下以适应我的情况 - 我正在寻找一个值为null的属性的索引,而第五行代码导致跳过了该索引。 - David Brunow

2
您可以使用过滤方法。
 const filteredData = data.filter(e => e.name !== 'john');

1

你可以使用自定义函数作为参数来定义排序机制,使用 Array.sort 进行排序。

在你的例子中,它会给出:

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'

排序函数必须返回-1(如果a应该在b之前),1(如果a应该在b之后)或0(如果两者相等)。由您定义正确的逻辑来对数组进行排序。

您错过了问题的最后一部分,即想要知道索引。正如其他人所说,您必须循环遍历数组才能找到它。


1

只需遍历您的数组并找到位置:

var i = 0;
for(var item in Data) {
    if(Data[item].name == 'John')
        break;
    i++;
}
alert(i);

这应该是 if(Data[item].name == 'John'),我已经为您修复了。 - gen_Eric
这里有一个要点。如果“not found”变量i包含正索引。并且要测试“not found”,需要比较i===Data.length。 - Andrew D.
第二时刻是当数组包含非索引器自定义属性时。例如: var Data[1,2,3,4,5]; Data["test"]=7897; 比较以下输出:for(var i in Data)alert(Data[i]);for(var i=0;i<Data.length;i++)alert(Data[i]); - Andrew D.
但是没有显式循环的方法吗? - Peter Mortensen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接