对象数组中的indexOf方法?


512

获取包含对象的数组的索引的最佳方法是什么?

想象一下这种情况:

var hello = {
    hello: 'world',
    foo: 'bar'
};
var qaz = {
    hello: 'stevie',
    foo: 'baz'
}

var myArray = [];
myArray.push(hello,qaz);

现在,我想拥有indexOf对象对象,该对象的hello属性'stevie'在此示例中为1

我是JavaScript的新手,我不知道是否有一个简单的方法,或者是否应该构建自己的函数来做到这一点。


1
是否要合并两个对象helloqaz
阿明

不,我没有。我想在数组中有一个对象列表。
安东尼奥·拉古纳

啊好吧!您想知道整个对象在数组中的位置,该数组具有已定义的属性。
阿明

10
我找到了一个非常简单的函数来解决这个确切的问题,例如:var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor); var objectFound = array[elementPos]; [link](stackoverflow.com/a/16100446/1937255
Rick

ES6 Array.indexOf优于公认的答案(如果ES6对您
有用

Answers:


1042

我认为您可以使用map函数在一行中解决它:

pos = myArray.map(function(e) { return e.hello; }).indexOf('stevie');

58
老实说,这应该是公认的答案。如今,大多数浏览器都支持Array.prototype.map()
AlbertEngelB 2013年

10
IE8不支持它,但是,如果这不是问题,那么这是最好的解决方案。
Antonio Laguna

57
嗯...值得注意的是Array.prototype.map()创建了一个包含映射项的全新数组吗?因此,如果您有一个包含1000个元素的数组,那么您首先创建了另一个包含1000个元素的数组,然后进行搜索?我认为值得一看的是这种方法与简单的for循环的性能。特别是在资源有限的移动平台上运行时。
2015年

7
@Doug尽管您对性能的观点的确是正确的,但在他们认为概要分析瓶颈的应用程序中,谁将正确地用一行七行代码替换几乎定义为IO /网络的应用程序?
Jared Smith

6
从技术上讲,缩小的js文件也是一行; D
greatKing

371

除IE(非边缘)以外的所有浏览器均支持Array.prototype.findIndex。但是提供的polyfill很好。

var indexOfStevie = myArray.findIndex(i => i.hello === "stevie");

地图的解决方案是可以的。但是您每次搜索都会遍历整个数组。对于findIndex来说,这只是最坏的情况,一旦找到匹配,它将停止迭代。


没有真正简洁的方法 (当开发人员不得不担心IE8时),但这是一个常见的解决方案:

var searchTerm = "stevie",
    index = -1;
for(var i = 0, len = myArray.length; i < len; i++) {
    if (myArray[i].hello === searchTerm) {
        index = i;
        break;
    }
}

或作为功能:

function arrayObjectIndexOf(myArray, searchTerm, property) {
    for(var i = 0, len = myArray.length; i < len; i++) {
        if (myArray[i][property] === searchTerm) return i;
    }
    return -1;
}
arrayObjectIndexOf(arr, "stevie", "hello"); // 1

只是一些注意事项:

  1. 不要在数组循环中使用...
  2. 找到“针头”后,请务必打破循环或退出功能
  3. 注意对象相等

例如,

var a = {obj: 0};
var b = [a];
b.indexOf({obj: 0}); // -1 not found

该函数的searchterm比较错误,因为它应该是searchTerm :)
Antonio Laguna

有多次发生

3
@SteveBennett是性能优化版本;数组的长度仅需确定一次(初始化for循环的变量时)。在您的情况下,将在每次迭代时重新检查长度。另请参见stackoverflow.com/questions/5349425/…stackoverflow.com/questions/8452317/… 但是,如果性能不高,那么这并不重要。
Loother

1
好的答案,但是我做了一些性能基准测试(请参阅jsperf.com/find-index-of-object-in-array-by-contents),并且发现此处提到的基于函数的答案似乎是性能第二高的答案。性能更高的唯一结果就是将其放入原型,而不仅仅是我的回答中提到的函数。
单音

123

在ES2015中,这非常简单:

myArray.map(x => x.hello).indexOf('stevie')

或者,对于较大的阵列,可能具有更好的性能:

myArray.findIndex(x => x.hello === 'stevie')

2
使用ES6的好方法
kag

正如我的回答中所提到的那样,我惊讶的是这些方法都不如原型for循环那样高效。即使findIndex方法对浏览器的支持有些欠佳,似乎它会做同样的事情,但是仍然表现不佳?请参阅我的答案中的链接以获取基准。
单音

很高兴知道,性能对于手头的任务是否重要。根据我的经验,它很少是ymmv。
史蒂夫·本内特

24
var idx = myArray.reduce( function( cur, val, index ){

    if( val.hello === "stevie" && cur === -1 ) {
        return index;
    }
    return cur;

}, -1 );

17

我喜欢Pablo的回答,但是Array#indexOf和Array#map并非在所有浏览器上都起作用。下划线将使用本机代码(如果可用),但也具有后备功能。另外,它具有可实现Pablo匿名地图方法的功能的pluck方法。

var idx = _.chain(myArray).pluck("hello").indexOf("Stevie").value();

1
Array.prototype.map()被用于soported IE9 +,你可以使用一个填充工具用于IE8,7,6: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
约翰查瓦里亚

1
您可以使用polyfill。。。或者,您也可以只使用下划线或lodash,它们基本上是polyfill,并附有许多其他东西。下划线有什么异议?尺寸?
tandrewnichols 2014年

我非常喜欢Underscore,您的回答也很聪明,但是恕我直言,Pablo的回答是最干净的。
约翰·埃查瓦里亚

哇,我从没想过要像那样使用链接。我真的很喜欢它使搜索变得流畅。
Dylan Pierce

chain这里是多余的。_.pluck(myArray, 'hello').indexOf('stevie')
史蒂夫·本内特

14

或原型:

Array.prototype.indexOfObject = function arrayObjectIndexOf(property, value) {
    for (var i = 0, len = this.length; i < len; i++) {
        if (this[i][property] === value) return i;
    }
    return -1;
}

myArr.indexOfObject("name", "stevie");

9
很方便!虽然我会选择prototype.indexOfObject,以免干扰现有的Array.indexOf方法。 Array.prototype.indexOfObject = function(property, value) { for (var i = 0, len = this.length; i < len; i++) { if (this[i][property] === value) return i; } return -1; };
亚当,

1
我将其包装在一个自动执行的闭包中,其中旧的存储在事先存储的空间中,替换函数的第一行沿的方向if (typeof property === 'string' || typeof property === 'number' || typeof property === 'boolean') return oldIndexOf(property, value);。这是因为这些是不可变的几种类型。如果需要,我还将使用第三个参数来启用对本机方法的回退。
伊西亚·梅多斯

8

简要

myArray.indexOf('stevie','hello')

用例 :

  /*****NORMAL****/  
[2,4,5].indexOf(4) ;//OUTPUT 1
 /****COMPLEX*****/
 [{slm:2},{slm:4},{slm:5}].indexOf(4,'slm');//OUTPUT 1
 //OR
 [{slm:2},{slm:4},{slm:5}].indexOf(4,function(e,i){
   return e.slm;
});//OUTPUT 1
/***MORE Complex**/
[{slm:{salat:2}},{slm:{salat:4}},{slm:{salat:5}}].indexOf(4,function(e,i){
   return e.slm.salat;
});//OUTPUT 1

API:

    Array.prototype.indexOfOld=Array.prototype.indexOf

    Array.prototype.indexOf=function(e,fn){
      if(!fn){return this.indexOfOld(e)}
      else{ 
       if(typeof fn ==='string'){var att=fn;fn=function(e){return e[att];}}
        return this.map(fn).indexOfOld(e);
      }
    };

6

我在这里对各种答案进行了性能测试,任何人都可以自行运行它们:

https://jsperf.com/find-index-of-object-in-array-by-contents

根据我在Chrome中进行的初始测试,以下方法(使用原型内设置的for循环)是最快的:

Array.prototype.indexOfObject = function (property, value) {
    for (var i = 0, len = this.length; i < len; i++) {
        if (this[i][property] === value) return i;
    }
    return -1;
}

myArray.indexOfObject("hello", "stevie");

这段代码是Nathan Zaetta的答案的稍作修改的版本。

在性能基准测试中,我尝试将目标放在1000个对象数组的中间(索引500)和末尾(索引999)中,即使我将目标作为数组中的最后一项放置(意思是它必须先遍历数组中的每个项目,然后才能以最快的速度结束。

该解决方案还具有成为重复执行最简洁的优点,因为仅需要重复最后一行:

myArray.indexOfObject("hello", "stevie");

2
我正准备用我自己的测验用小提琴发布该问题的答案,但是由于您的回答,我不再需要了。我只想确认您的测试-我得到了相同的结果,但是使用while循环而不是一个循环for,并且performance.now()。我希望这个答案能得到更多支持,而且我希望早些看到它,它可以为我节省一些时间...
Yin Cognyto


5

虽然,这里的大多数其他答案都是有效的。有时,最好只是在使用位置附近做一个简短的简单函数。

// indexOf wrapper for the list of objects
function indexOfbyKey(obj_list, key, value) {
    for (index in obj_list) {
        if (obj_list[index][key] === value) return index;
    }
    return -1;
}
// Find the string in the list (default -1)
var test1 = indexOfbyKey(object_list, 'name', 'Stevie');
var test2 = indexOfbyKey(object_list, 'last_name', 'some other name');

这取决于对您而言重要的内容。它可能节省代码行,并且非常聪明,因此可以使用单行代码,或者将通用解决方案放在涵盖各种边缘情况的地方。但是有时候,最好只说一句:“在这里我是这样做的”,而不是让将来的开发人员去做额外的逆向工程。特别是如果您认为自己是“新手”,例如您的问题。


4
array.filter(function(item, indx, arr){ return(item.hello === 'stevie'); })[0];

注意[0]

像这样使用是正确reduceAntonio Laguna的答案中的。

为简短而道歉...


4

如果您的对象与您在数组中使用的对象相同,则应该能够像获取字符串一样获取对象的索引。

var hello = {
    hello: 'world',
    foo: 'bar'
};
var qaz = {
    hello: 'stevie',
    foo: 'baz'
}

var qazCLONE = { // new object instance and same structure
    hello: 'stevie',
    foo: 'baz'
}

var myArray = [hello,qaz];

myArray.indexOf(qaz) // should return 1
myArray.indexOf(qazCLONE) // should return -1

这是我一直在寻找的答案,因为对于IndexOf是否按值或什么匹配,我仍然不清楚。现在,我知道可以使用IndexOf查找我的对象,不用担心是否存在其他具有相同属性的对象。
MDave


3

简单:

myArray.indexOf(myArray.filter(function(item) {
    return item.hello == "stevie"
})[0])

3

如果您只想找到职位,请参阅@Pablo的答案

pos = myArray.map(function(e) { return e.hello; }).indexOf('stevie');

但是,如果您希望找到该元素(即,如果您正在考虑做这样的事情myArray[pos]),则可以使用一种更有效的单行方法来完成filter

element = myArray.filter((e) => e.hello === 'stevie')[0];

查看性能结果(〜+ 42%ops / sec):http : //jsbench.github.io/#7fa01f89a5dc5cc3bee79abfde80cdb3


2

参见以下示例:http : //jsfiddle.net/89C54/

for (i = 0; i < myArray.length; i++) {
    if (myArray[i].hello === 'stevie') {
        alert('position: ' + i);
        return;
    }
}

它从零开始计数。


2

我已经做了一个通用的功能来检查下面的代码并适用于任何对象

function indexOfExt(list, item) {
    var len = list.length;

    for (var i = 0; i < len; i++) {
        var keys = Object.keys(list[i]);
        var flg = true;
        for (var j = 0; j < keys.length; j++) {
            var value = list[i][keys[j]];
            if (item[keys[j]] !== value) {
                flg = false;
            }
        }
        if (flg == true) {
            return i;
        }
    }
    return -1;
}

var items = [{ "hello": 'world', "foo": 'bar' }];
var selectedItem = { "hello": 'world', "foo": 'bar' };
alert(items.indexOf(selectedItem));
alert(indexOfExt(items, selectedItem));

第一个警报将返回-1(未找到匹配项),第二个警报将返回0(已找到匹配项)。


2

_.findIndexunderscore.js库中使用

这是例子 _.findIndex([{a:1},{a: 2,c:10},{a: 3}], {a:2,c:10}) //1


如果建议其他库中的方法,则应提及它们的来源。
Craicerjack

@Steve Bennett很好地使用了图书馆。现在它位于lodash
zabusa

2

使用ES6 findIndex方法,无需使用lodash或任何其他库,您可以编写:

function deepIndexOf(arr, obj) {
  return arr.findIndex(function (cur) {
    return Object.keys(obj).every(function (key) {
      return obj[key] === cur[key];
    });
  });
}

这将比较对象的立即属性,但不会递归到属性中。

如果您的实现尚不提供findIndex(大多数情况不提供),则可以添加支持此搜索的轻型polyfill:

function deepIndexOf(arr, obj) {
  function findIndex = Array.prototype.findIndex || function (pred) {
    for (let i = 0; i < this.length; ++i) {
      if (pred.call(this, this[i], i)) {
        return i;
      }
    }

    return -1;
  }

  return findIndex.call(arr, function (cur) {
    return Object.keys(obj).every(function (key) {
      return obj[key] === cur[key];
    });
  });
}

(根据我对这个骗子的回答)


2

Array.prototype.findIndex()基本上可以使用本机便捷的功能:

如果数组中的元素满足提供的测试功能,则findIndex()方法将返回数组中的索引。否则返回-1。

请注意,Internet Explorer,Opera和Safari不支持它,但是您可以使用下面的链接中提供的Polyfill。

更多信息:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex

var hello = {
  hello: 'world',
  foo: 'bar'
};
var qaz = {
  hello: 'stevie',
  foo: 'baz'
}

var myArray = [];
myArray.push(hello, qaz);

var index = myArray.findIndex(function(element, index, array) {
  if (element.hello === 'stevie') {
    return true;
  }
});
alert('stevie is at index: ' + index);


2

Furthor的@Monika加尔格答,你可以使用findIndex()(有一种填充工具用于unsupprted浏览器)。

我看到人们拒绝了这个答案,我希望他们这样做是因为语法错误,因为我认为这是最优雅的方法。

如果数组中的元素满足提供的测试功能,则findIndex()方法将返回数组中的索引。否则返回-1。

例如:

var hello = {
  hello: 'world',
  foo: 'bar'
};
var qaz = {
  hello: 'stevie',
  foo: 'baz'
}

var myArray = [];
myArray.push(hello,qaz);

var index = myArray.findIndex(function(element) {
  return element.hello == 'stevie';
});

alert(index);


该方法看起来很优雅,但是根据MDN,没有IE支持吗?developer.mozilla.org/en/docs/Web/JavaScript/Reference/...
亚科·卡尔胡

尝试使用polyfill(答案中的链接)。
Mosh Feu

1

这是在数组中查找对象索引的方法

    var myArray = [{  hello: 'world',
        foo: 'bar'
    },{
        hello: 'stevie',
        foo: 'baz'
    }];



    for (i = 0; i < myArray.length; i++) {
        if (myArray[i].hello === 'stevie') {
            alert('position: ' + i);
            return;
        }
    }

0

无需自定义代码即可使用

var arr, a, found;
arr = [{x: 1, y: 2}];
a = {x: 1, y: 2};
found = JSON.stringify(arr).indexOf(JSON.stringify(a)) > - 1;
// found === true

注意:这并没有给出实际的索引,它只是告诉您对象是否存在于当前数据结构中


1
这是无效的,因为它不允许获取任何索引
Antonio Laguna

0

您可以简单地使用

const someId = 2;
const array = [{id:1}, {id:2}, {id:3}];
const index = array.reduce((i, item, index) => item.id === someId ? index : i, -1);
alert('someId ' + someId + ' is at index ' + index);

没有下划线,没有,只是减少。


0
var hello = {hello: "world",  foo: "bar"};
var qaz = {hello: "stevie", foo: "baz"};
var myArray = [];
myArray.push(hello,qaz);

function indexOfObject( arr, key, value   ) {
    var j = -1;
    var result = arr.some(function(obj, i) { 
        j++;
        return obj[key] == value;
    })

    if (!result) {
        return -1;
    } else {
        return j;
    };
}

alert(indexOfObject(myArray,"hello","world"));

使用数组一些方法。
user3235365 '11


-2

我将更喜欢使用findIndex()方法:

 var index = myArray.findIndex('hello','stevie');

index 将为您提供索引号。


1
答案,拼写和代码缩进以及:)是错误的吗?
Sachin Verma 2014年

1
findIndex不在任何javascript标准实现中。有一种即将到来的(ecma 6)提案针对这种方法,但是其签名并非如此。请阐明您的意思(也许是方法的名称),提供findIndex方法声明或命名您正在使用的库。
Sebastien F. 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.