【问题标题】:Find last index of element inside array by certain condition通过特定条件查找数组内元素的最后一个索引
【发布时间】:2016-12-02 09:54:16
【问题描述】:

假设我有一个对象数组:

[
  { 'a': 'something',     'b':12 },
  { 'a': 'something',     'b':12 },
  { 'a': 'somethingElse', 'b':12 },
  { 'a': 'something',     'b':12 },
  { 'a': 'somethingElse', 'b':12 }
]

获取a 的值为'something' 的元素的最后一个索引(在本例中为索引3)的最简洁方法是什么?有什么办法可以避免循环吗?

【问题讨论】:

标签: javascript arrays


【解决方案1】:

这是一个可重复使用的 typescript 版本,它反映了 ES2015 findIndex 函数的签名:

/**
* Returns the index of the last element in the array where predicate is true, and -1
* otherwise.
* @param array The source array to search in
* @param predicate find calls predicate once for each element of the array, in descending
* order, until it finds one where predicate returns true. If such an element is found,
* findLastIndex immediately returns that element index. Otherwise, findLastIndex returns -1.
*/
export function findLastIndex<T>(array: Array<T>, predicate: (value: T, index: number, obj: T[]) => boolean): number {
    let l = array.length;
    while (l--) {
        if (predicate(array[l], l, array))
            return l;
    }
    return -1;
}

【讨论】:

  • 没有副本,没有反向,没有无用的过滤器,O(n)。确定的。
  • 问题在 js 中提问,所以答案应该在 js 中,即使 ts 和 js 是可转换的。喜欢:stackoverflow.com/a/33269005/5858238
  • 您可以将所有类型信息放在 JSDoc 注释中。此外,它不会反映签名 - 您缺少 thisArg
【解决方案2】:

您可以使用findIndex 获取索引。这将为您提供第一个索引,因此您必须反转数组。

var d = [{'a': "something", 'b':12}, {'a': "something", 'b':12}, {'a': "somethingElse", 'b':12}, {'a': "something", 'b':12}, {'a': "somethingElse", 'b':12}]

function findLastIndex(array, searchKey, searchValue) {
  var index = array.slice().reverse().findIndex(x => x[searchKey] === searchValue);
  var count = array.length - 1
  var finalIndex = index >= 0 ? count - index : index;
  console.log(finalIndex)
  return finalIndex;
}

findLastIndex(d, 'a', 'something')
findLastIndex(d, 'a', 'nothing')

【讨论】:

  • 它有效,但我不明白。你对没有参数的切片做了什么?你能解释一下逻辑吗?
  • @IvanLencina array.reverse 反转一个数组,所以为了防止修改原始数组,我使用 slice 复制了它
  • 哦,我明白了。谢谢 :) 我实施了您的解决方案
  • 如果未找到元素 (-1),finalIndex 将是数组的大小,而不是 -1。需要添加该案例才能完成。
  • 它的功能更强大,但切片和反转数组会比向后迭代慢得多
【解决方案3】:
let newArray = yourArray.filter((each)=>{
    return (each.a === something)
});
newArray[newArray.length-1];

你也可以这样做

let reversedArray = yourArray.reverse();
reversedArray.find((each)=>{return each.a === something})

【讨论】:

【解决方案4】:

如果找到,您可以从末尾迭代并退出循环。

var data = [{ a: 'something', b: 12 }, { a: 'something', b: 12 }, { a: 'somethingElse', b: 12 }, { a: 'something', b: 12 }, { a: 'somethingElse', b: 12 }],
    l = data.length;

while (l--) {
    if (data[l].a ==='something') {
        break;
    }
}

console.log(l);

【讨论】:

  • 尽可能不循环?
  • 不,不是没有任何循环。并且与右边的其他阵列方法没有短路。
【解决方案5】:

对我来说,反转数组听起来不是很简单,所以我对非常相似的情况的解决方案是使用map()lastIndexOf()

var lastIndex = elements.map(e => e.a).lastIndexOf('something');

更新:来自欺骗帖子的this answer 使map(cond).lastIndexOf(true) 变得更好

更新。:虽然这不是最有效的解决方案,因为我们必须始终遍历所有数组,而如果我们从末尾运行,我们可以在找到第一个匹配项的那一刻结束搜索(@nico-timmerman 的答案)。

【讨论】:

    【解决方案6】:

    你可以使用 Lodash:

    import findLastIndex from "lodash/findLastIndex"
    
    const data = [
      { a: 'something', b: 12 },
      { a: 'something', b: 12 },
      { a: 'somethingElse', b: 12 },
      { a: 'something', b: 12 },
      { a: 'somethingElse', b: 12 },
    ]
    
    const lastIndex = findLastIndex(data, v => v.a === "something")
    

    【讨论】:

      【解决方案7】:

      在 ES6 中这样的东西怎么样:

        arr.indexOf(arr.filter(item => item.a === 'something').pop())
      

      【讨论】:

        【解决方案8】:

        首先,这不是一个对象数组,而是一个数组数组。对象数组如下所示:

        [{'a': something, 'b':12},
        {'a': something, 'b':12},
        {'a': somethingElse, 'b':12},
        {'a': something, 'b':12},
        {'a': somethingElse, 'b':12}]
        

        通常,在使用非数字索引时使用对象语法是一种很好的做法。

        其次,要回答您的问题,您可以使用反向循环:

        for(let i=(arr.length - 1); i>=0; i--){
            if(arr[i].a === "something"){
                index = i;
                break;
            }
        }
        

        【讨论】:

        • @ZoltánTamási 知道了
        【解决方案9】:

        我想知道为什么现在很多人总是想避免循环。它们与ifs 和纯代码行序列一样具有自然结构。为了避免重复你自己,为它写一个函数,你甚至可以添加到Array.prototype。下面是一个简单的例子,没有经过测试,仅供参考。

        获取索引

        Array.prototype.lastIndex = function(cond) {
          if (!this.length) return -1;
          if (!cond) return this.length-1;
        
          for (var i=this.length-1; i>=0; --i) {
            if (cond(this[i])) return i;
          }
        
          return -1;
        }
        

        或者直接用于元素

        Array.prototype.lastOrDefault = function(cond, defaultValue) {
          if (!this.length) return defaultValue;
          if (!cond) return this[this.length-1];
        
          for (var i=this.length-1; i>=0; --i) {
            if (cond(this[i])) return this[i];
          }
        
          return defaultValue;
        }
        

        使用示例:

        myArr = [1,2,3,4,5];
        var ind1 = myArr.lastIndex(function(e) { return e < 3; }); 
        var num2 = myArr.lastOrDefault(function(e) { return e < 3; });
        var num8 = myArr.lastOrDefault(function(e) { return e > 6; }, /* explicit default */ 8);
        

        【讨论】:

        • 对不起,在我的第一个版本中,我没有考虑到 OP 想要获取索引。我也为此添加了一个版本。
        • 你能告诉我(!cond)是什么意思吗?
        • @MathCoder !cond 表示 condfalsey。在这种特殊情况下,我使用它来允许在不指定任何条件函数的情况下使用该函数,并且在这种情况下,它返回数组的最后一个索引或最后一个元素(或者如果数组为空,则返回给定的默认值)。一般来说,它只是一个前置条件检查,也可能会抛出错误,但我发现我也可以为这种情况弥补一些语义。
        【解决方案10】:

        请看看这个方法。

        var array=[{a: 'something', b:12},
                   {a: 'something', b:12},
                   {a: 'somethingElse', b:12},
                   {a: 'something', b:12},
                   {a: 'somethingElse', b:12}
                  ];
        
        console.log(array.filter(function(item){
        		return item.a=='something';
        }).length);

        【讨论】:

        • 反向/过滤器基本上是一个循环。仅仅因为它隐藏在方法后面并不会改变这一点。
        • @EvanTrimboli 在没有循环的情况下无法在数组中找到元素,即使 indexOf 方法使用 while 循环
        • 我知道,答案的原文说“这是没有循环的”,这并不正确。
        • 这个答案在我看来是错误的。代码 sn-p 中的结果是偶然的。交换最后两行以了解我的意思。
        【解决方案11】:

        我发现最干净的方法是使用单个 reduce。无需反转数组或使用多个循环

        const items = [
          {'a': 'something', 'b': 12},
          {'a': 'something', 'b': 12},
          {'a': 'somethingElse', 'b': 12},
          {'a': 'something', 'b': 12},
          {'a': 'somethingElse', 'b': 12}
        ];
        
        function findLastIndex(items, callback) {
          return items.reduce((acc, curr, index) => callback(curr) ? index : acc, 0);
        }
        
        console.log(findLastIndex(items, (curr) => curr.a === "something"));
        

        【讨论】:

          【解决方案12】:

          这是我用的:

          const lastIndex = (items.length -1) - (items.reverse().findIndex(el=> el.a === "something"))
          
          

          【讨论】:

          • 不完全!这只是返回 n-1,其中 n 是满足条件的项目数。它不返回最后一个元素的?????。
          • 是的,你是对的,我更新了我的答案。谢谢
          • 这不支持-1案例
          【解决方案13】:

          更新 - Array.prototype.findLastIndex 现在可以使用了 -

          var d = [{'a': "something", 'b':12}, {'a': "something", 'b':12}, {'a': "somethingElse", 'b':12}, {'a': "something", 'b':12}, {'a': "somethingElse", 'b':12}]
          
          const lastIdx = d.findLastIndex(n => n.a === 'something');
          console.log(lastIdx);

          **在使用之前请查看link,了解哪些浏览器支持findLastIndex

          您也可以使用reversefindIndex 实现此目的,但性能不如使用findLastIndex

          var d = [{'a': "something", 'b':12}, {'a': "something", 'b':12}, {'a': "somethingElse", 'b':12}, {'a': "something", 'b':12}, {'a': "somethingElse", 'b':12}]
          
          const lastIdx = d.reverse().findIndex(n => n.a === 'something');
          
          console.log(lastIdx);

          findLastIndex - https://github.com/tc39/proposal-array-find-from-last

          findIndex - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex

          反向 - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse

          【讨论】:

            【解决方案14】:
            var arr = [
               {'a': 'something', 'b':12},
               {'a': 'something', 'b':12},
               {'a': 'somethingElse', 'b':12},
               {'a': 'something', 'b':12},
               {'a': 'somethingElse', 'b':12}
            ];
            
            var item_count = 0;
            var traverse_count = 0;
            var last_item_traverse_count = 0;    
            arr = arr.reverse();
            
            arr.filter(function(element) {
               traverse_count += 1;
               if(item_count < 1 && element.a == 'something') {
                   last_item_traverse_count = traverse_count;
                   item_count += 1;
                   return true;
               }
            
               return false;
            });
            
            var item_last_index = arr.length - last_item_traverse_count;
            
            console.log(item_last_index);
            

            我希望这段代码确实有效。代码可能不遵循命名约定。对此我很抱歉。您可以随意命名这些变量。

            【讨论】:

              【解决方案15】:

              这里是我用来获取最后一个活动元素的:

              return this._scenarios.reverse().find(x => x.isActive);
              

              【讨论】:

              • reverse 改变数组,这可能是 OP 不想要的副作用。而且效率特别低!
              【解决方案16】:

              简单快速:

              const arr = [
                {'a': 'something', 'b': 12},
                {'a': 'something', 'b': 12},
                {'a': 'somethingElse', 'b': 12},
                {'a': 'something', 'b': 12},
                {'a': 'somethingElse', 'b': 12}
              ];
              
              let i = arr.length;
              while(i--) if (arr[i] === 'something') break;
              
              console.log(i);  //last found index or -1
              
              

              【讨论】:

              • 您确定代码有效吗?特别是对于似乎错误的 while 条件。不是while(i-- &gt; 0)
              • 这是最好的答案。您唯一的解决方案结构良好且速度快
              【解决方案17】:

              你可以同时使用reverse和map来防止改变原始数组。

              var arr = [{'a': 'something', 'b':12},
              {'a': 'something', 'b':12},
              {'a': 'somethingElse', 'b':12},
              {'a': 'something', 'b':12},
              {'a': 'somethingElse', 'b':12}];
              
              var index = arr.length - 1 - arr.map(x => x)
                 .reverse()
                 .findIndex(x => (x.a === 'something'))
              if (index === arr.length) {
                index = -1;
              }
              

              【讨论】:

              • 但是如果对象不存在会发生什么。它返回数组的长度而不是“-1”。所以所有对象 a 的值 = 'something' 和所有对象 a 的值 != 'something' 返回相同的结果(数组的长度)。
              • @sachinkumar 好点,但你知道,在这种情况下,如果你得到 5,它不能是索引,因为在这种情况下你的数组索引是从 0 到 4。如果你想得到 - 1 当你搜索时,你可以在底部多加一行来检查是否没有找到字符串。我也编辑了我的答案以具有该功能。
              【解决方案18】:

              const arrSome = [{'a': 'something', 'b':12},
                  {'a': 'something', 'b':12},
                  {'a': 'somethingElse', 'b':12},
                  {'a': 'something', 'b':12},
                  {'a': 'somethingElse', 'b':12}]
              
              const lastIndex = arrSome.reverse().findIndex(el=> el.a == 'something');//?
              let index;
              if (~lastIndex) {
                  index =arrSome.length - lastIndex;
              } else {
                  index = -1
              }
              
              console.log(index) 

              【讨论】:

                【解决方案19】:

                您可以使用 Math.max 应用到与您的条件匹配的数组 Array.prototype.map()

                喜欢这个例子

                private findLastIndex() {
                    const filter = this.myArray.map((t, index) => {
                      if (
                        // your condition
                      ) {
                        return index;
                      } else {
                        return -1;
                      }
                    });
                
                    return Math.max.apply(null, filter);
                  }
                

                【讨论】:

                  【解决方案20】:

                  这是一个不使用(手动)循环且不改变调用数组的解决方案:

                  const arr = [
                    {'a': 'something', 'b': 12},
                    {'a': 'something', 'b': 12},
                    {'a': 'somethingElse', 'b': 12},
                    {'a': 'something', 'b': 12},
                    {'a': 'somethingElse', 'b': 12}
                  ];
                  
                  function findLastIndex(arr, callback, thisArg) {
                    return (arr.length - 1) - // Need to subtract found, backwards index from length
                      arr.reduce((acc, cur) => [cur, ...acc], []) // Get reversed array
                      .findIndex(callback, thisArg); // Find element satisfying callback in rev. array
                  }
                  
                  console.log(findLastIndex(arr, (e) => e.a === "something"));

                  参考:

                  需要注意的是这里使用的reducer是vastly outperformed by arr.slice().reverse(),和@Rajesh's answer中使用的一样。

                  【讨论】:

                    【解决方案21】:

                    您可以将列表映射到 a 然后使用 lastIndexOf:

                    const array = [
                      {'a': 'something', 'b':12},
                      {'a': 'something', 'b':12},
                      {'a': 'somethingElse', 'b':12},
                      {'a': 'something', 'b':12},
                      {'a': 'somethingElse', 'b':12}
                    ];
                    
                    const result = array.map(({a}) => a).lastIndexOf('something');
                    console.log(result);

                    【讨论】:

                      【解决方案22】:

                      更新 - 2021 年 10 月 27 日(Chrome 97+)

                      您现在可以使用findLastIndex

                      const array = [
                        {'a': 'something', 'b':12},
                        {'a': 'something', 'b':12},
                        {'a': 'somethingElse', 'b':12},
                        {'a': 'something', 'b':12},
                        {'a': 'somethingElse', 'b':12}
                      ];
                      
                      const last_index = array.findLastIndex((item) => item.a === 'something');
                      // → 3
                      

                      阅读更多here

                      【讨论】:

                      • 请记住,一些主流浏览器(如 firefox 和 safari)尚不支持此功能:caniuse.com/?search=findLastIndex%20
                      • Tnx!我现在更新了我的答案以突出显示 Chrome。
                      【解决方案23】:

                      为什么不直接获取长度并用作数组指针,例如

                      var arr = [ 'test1', 'test2', 'test3' ];
                      

                      然后通过获取数组的长度减去'1'来获取数组的最后一个索引,因为数组索引总是从'0'开始

                      var last arrIndex = arr[arr.length - 1];
                      

                      【讨论】:

                      • 问题是找到最后一个索引,其中“a”的值为“something”,而不是最后一个值...
                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2012-11-03
                      • 1970-01-01
                      • 1970-01-01
                      • 2010-11-06
                      • 1970-01-01
                      • 2022-01-03
                      相关资源
                      最近更新 更多