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


241

例如,我有:

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'以获取属性标记的值。

我该如何解决这个问题?


为什么要在搜索属性之前先对列表进行排序?
JJJ

JavaScript对象是{Key:Value},我已为您修复了它。
火箭Hazmat


如果您仔细阅读答案,则看起来好像有一些本机Data对象。它只是一个大写的变量名,与约定不符。如果其他人对此感到不安,我将对问题和答案进行修改以更正此命名。
罗伊·普林斯

Answers:


170

正如其他答案所暗示的那样,遍历数组可能是最好的方法。但是我会把它放在它自己的函数中,并使它更抽象一些:

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 2015年

3
对于那些对此解决方案有疑问的人,请记住===等号不仅会检查值,还会检查数据类型。因此,如果实际值是字符串,则比较日期,数字和空值可能会返回false。如果数据类型对您而言不是很重要,则可以使用==等号。
David Addoteye

不要这样做。了解如何使用高阶函数,这是旧的处理方式。
带领

327

由于排序部分已经回答。我将提出一种优雅的方法来获取数组中属性的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');

Array.prototype.map在IE7或IE8上不可用。ES5兼容性

这就是ES6和arrow语法,它更简单:

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

4
无论如何,我都必须迭代整个数组。第一种方法不需要。
德国阿塔纳西奥

1
很简单,但是要考虑使用大型数据集时的性能
gavioto 2015年

1
真的很棒的答案帮助我解决了这个问题!
NiallMitch14年

1
这个解决方案很可爱。先生,愿你活到一千年。
深度元素

3
在ES6版本中进行映射,然后进行indexOf确实意味着它们将在数组上循环两次。相反,您应该使用我的答案中所示的findIndex方法
silverlight513

209

如果您适合使用ES6。数组现在具有findIndex函数。这意味着您可以执行以下操作:

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

6
放下最直接,最优雅的解决方案,我不会说findIndex是贪婪的(在我的情况下是完美的),但是用户要知道,如果您的对象具有重复的值(例如,两个对象带有name: John),则只会返回首场比赛
GrayedFox '17

太棒了!! 谢谢。
moreirapontocom,

@GrayedFox你不是说不贪心吗?贪婪意味着更多结果,而findIndex仅返回第一个匹配项。
乔斯

1
对不起,@乔斯,但我想您不太了解贪婪的搜索算法的作用。贪心算法仅考虑其手头的信息,例如,在只需要一个结果的情况下,搜索列表中的一项就非常有效。findIndex方法很贪心,因为它仅返回第一个匹配项。如果不贪心,它将继续搜索。您正在考虑日常英语中使用的“贪婪”一词,但在编程(即SO)的上下文中,这与您的想法相反;)
GrayedFox

感谢您解释@GrayedFox。我对贪婪的引用实际上也是从编程中获取的,但与正则表达式相关,贪婪比非贪婪需要更多的字符!:-)
乔斯(Jos)

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

这是以下内容的简化版本:

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

来自mozilla.org:

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


2
使用“ ===”进行字符串比较,这是此答案中唯一缺少的内容。
布鲁诺·塔瓦雷斯

@BrunoTavares,您认为在这种情况下哪种情况会有所不同?
edank

1
@edank如果OP将检查该token字段而不是该name字段,则可能存在问题,如Data[0].token == 312312评估true,但Data[0].token === 321321评估为false
kem


23

如果您在使用IE时遇到问题,则可以使用9.0及更高版本支持的map()函数:

var index = Data.map(item => item.name).indexOf("Nick");

1
以下答案提供了更多信息,并在第一时间发布。
亚历山大,

这绝对是最好的解决方案。
有益的

4

我唯一知道的方法是遍历所有数组:

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

在结果变量索引上包含对象的索引;如果未找到,则包含-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 desired object.

我必须对此稍作更改以适合我的情况-我正在寻找值为null的属性的索引,而第五行使它跳过了该索引。
大卫·布鲁诺

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'

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

编辑:错过了您想知道索引的问题的最后一部分。您将不得不遍历数组以找到其他人所说的。


1

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

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

应该是if(Data[item].name == 'John'),我已为您修复了。
火箭Hazmat

这有片刻。如果“未找到”变量,包含正指数。要测试“未找到”,需要比较是否i === Data.length
Andrew D.

第二点是数组是否包含非索引器自定义属性。例如:var Data [1,2,3,4,5]; Data [“ test”] = 7897; 比较以下输出:for(Data中的var i)alert(Data [i]); for(var i = 0; i <Data.length; i ++)alert(Data [i]);
Andrew D.


1

您为什么不使用一个小的解决方法?

创建一个以名称为索引的新数组。之后,所有搜索将使用索引。因此,只有一个循环。之后,您无需遍历所有元素!

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

现场例子。


这意味着首先循环遍历所有内容……然后修改对象(除非您克隆它们,但这会增加开销)。然后,您再次循环(至少是部分循环)。
乔斯

1

扩展了Chris Pickett的答案,因为在我的情况下,我需要搜索的深度不止一个属性级别:

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

您可以将“ attr1.attr2”传递给函数


1

那这个呢 ?:

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

假设您正在使用lodash或underscorejs。


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

如果您有一个内部包含多个对象的对象,如果您想知道某个对象是否包含在主对象中,只需将find(MasterObject,'Object to Search')放进去,此函数将返回响应(如果存在或不存在)(TRUE或FALSE) ),希望对此有所帮助,可以在JSFiddle上看到 示例


与答案添加一些解释如何回答帮助OP在固定电流问题
ρяσѕρєяķ

@ρяσsρєяK,谢谢您的建议,我已经添加了解释:)
Pedro Monteiro Arantes

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

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

尽管此代码段可以解决问题,但提供说明确实有助于提高您的帖子质量。请记住,您将来会为读者回答这个问题,而这些人可能不知道您提出代码建议的原因。也请尽量不要在代码中添加解释性注释,因为这会降低代码和解释的可读性!
再见StackExchange

1

如果要获取属性令牌的值,则也可以尝试此操作

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

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

_.findKey是lodash的函数


0

除了German Attanasio Ruiz的答案,您还可以使用Array.reduce()而不是Array.map()消除第二个循环。

var Data = [
    { name: 'hypno7oad' }
]
var indexOfTarget = Data.reduce(function (indexOfTarget, element, currentIndex) {
    return (element.name === 'hypno7oad') ? currentIndex : indexOfTarget;
}, -1);

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.