【问题标题】:How to determine if Javascript array contains an object with an attribute that equals a given value?如何确定 Javascript 数组是否包含具有等于给定值的属性的对象?
【发布时间】:2012-01-03 06:47:25
【问题描述】:

我有一个类似的数组

vendors = [{
    Name: 'Magenic',
    ID: 'ABC'
  },
  {
    Name: 'Microsoft',
    ID: 'DEF'
  } // and so on... 
];

如何检查此数组以查看“Magenic”是否存在?我不想循环,除非我必须这样做。我正在处理可能有几千条记录。

【问题讨论】:

  • @CAFxX 解决方案更好,如果您更新选定的解决方案会很棒。
  • 同意,之前没看到!
  • 您现在可以通过使用箭头函数进一步简化此操作。所有现代浏览器都支持这一点并且看起来更好。
  • @eMarine :OP 问题特别提到性能是首要问题。因此使用filtersome 虽然很漂亮,但不如使用显式循环好。 (由于必须为数组中的每个元素执行 lambda,它们会导致性能下降。)
  • 420 无法投票,但这个问题显示了研究工作,并且有用且清晰

标签: javascript arrays


【解决方案1】:

接受的答案仍然有效,但现在我们有一个 ECMAScript 6 原生方法 [Array.find][1][Array.some][2] 来实现相同的效果。

Array.some

使用some 如果您只想确定元素是否存在,即您需要true/false 确定。

引用 MDN:

some() 方法测试数组中的至少一个元素是否通过了提供的函数实现的测试。如果在数组中找到所提供函数为其返回 true 的元素,则返回 true;否则返回false。它不会修改数组。

Array.find

如果要从数组中获取匹配的对象,请使用 find 否则返回 undefined

引用 MDN:

find() 方法返回提供的数组中满足提供的测试功能的第一个元素的值。如果没有值满足测试函数,则返回 undefined。

var arr = [];
var item1 = {
    id: 21,
    label: 'Banana',
};
var item2 = {
    id: 22,
    label: 'Apple',
};
arr.push(item1, item2);

/* note : data is the actual object that matched search criteria 
  or undefined if nothing matched */

var data = arr.find(function(ele) {
    return ele.id === 21;
});

if (data) {
    console.log('found');
    console.log(data); // This is entire object i.e. `item` not boolean
}


/* note : doesExist is a boolean thats true or false depending on of whether the data was found or not */
var doesExist = arr.some(function(ele) {
    return ele.id === 21;
});


见我的jsfiddle link IE 有一个polyfill provided by mozilla

【讨论】:

  • 如果你只做 return ele.id == '2' 可能会更短,但是 +1 是一个好的 ES6 解决方案。
  • 很高兴有新的答案:) 只是想知道性能是否比上面的答案更好......
  • 我认为重要的是要指出'data'的返回值(当ele.id匹配一个id,例如'21')将是数组项本身(在这个情况下,整个项目对象)。如果期望数据变量结果是“真”或“假”而不是假值,那么您会非常失望。
  • 谢谢!我的任务有点不同。获取数组中对象的索引 => push if <0 || splice(index, 1) 这是我稍微更新的代码:const index = this.selected.indexOf(this.selected.find(s => s.id == passedObj.id))
  • 此代码仅适用于return ele.id === 21;;这是一个数字,而不是一个字符串。
【解决方案2】:

2021 解决方案*

Lodash .some (docs) 是一个干净的解决方案,如果您使用 _matchesProperty (docs) 速记:

_.some(VENDORS, ['Name', 'Magenic'])

说明

这将遍历VENDORS 数组,以查找具有Name 键值的字符串'Magenic' 的元素对象。一旦找到这个元素,它就会返回true 并停止迭代。如果遍历整个 Array 后没有找到该元素,则返回 false

代码 sn-p

const VENDORS = [{ Name: 'Magenic', ID: 'ABC' }, { Name: 'Microsoft', ID: 'DEF' }];

console.log(_.some(VENDORS, ['Name', 'Magenic'])); // true
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.20/lodash.min.js"></script>

* 请注意,这使用流行的lodash 库来实现最简单/最短的解决方案。对于那些感兴趣的人,我将其作为现有 vanilla JS 解决方案的替代方案。

【讨论】:

    【解决方案3】:

    无需重新发明 wheel 循环,至少无需明确(使用arrow functionsmodern browsers only):

    if (vendors.filter(e => e.Name === 'Magenic').length > 0) {
      /* vendors contains the element we're looking for */
    }
    

    或者,更好,因为它允许浏览器在找到一个匹配的元素后立即停止,所以它会更快:

    if (vendors.some(e => e.Name === 'Magenic')) {
      /* vendors contains the element we're looking for */
    }
    

    编辑:如果您需要与糟糕的浏览器兼容,那么最好的选择是:

    if (vendors.filter(function(e) { return e.Name === 'Magenic'; }).length > 0) {
      /* vendors contains the element we're looking for */
    }
    

    【讨论】:

    • @CAFxX 找到后如何获取索引?这甚至是一种可能性还是循环会更好地获取索引?
    • @Echtniet 如果您需要索引,那么vendors.findIndex 将为您提供第一个匹配元素的索引。相反,如果您需要该值,则 vendor.find 将给出第一个匹配元素,或者 vendor.filter 将给出所有匹配元素。你可以参考developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
    • 为什么some 更好
    • @7hibault 因为一旦找到带有name === "Magenic" 的对象,some 就会短路。使用filter,它将检查每个项目直到数组末尾并创建一个符合条件的新数组项目,然后检查length
    • 很多关于.some的cmets。现在是 2019 年,使用 .some 并使用 Polyfills 来支持糟糕的浏览器并继续你的生活...... polyfill.io/v3/url-builder。我唯一能看到的是,如果你不能支持箭头函数,那么它就像我提到的 Polyfill 一样简单:arr.some(function(i) { return i.Name === "Magenic" })
    【解决方案4】:

    函数mapfilterfind 和类似函数比简单循环慢得多。 对我来说,它们的可读性也比简单循环低,而且更难调试。使用它们看起来像是一种不合理的仪式。

    最好有这样的东西:

     arrayHelper = {
         arrayContainsObject: function (array, object, key){
             for (let i = 0; i < array.length; i++){
                if (object[key] === array[i][key]){
                     return true;
                }
             }
             return false;
         }
         
       };
    

    并在给定的 OP 示例中像这样使用它:

        vendors = [{
        Name: 'Magenic',
        ID: 'ABC'
         },
         {
        Name: 'Microsoft',
        ID: 'DEF'
         } 
      ];
    
    let abcObject = {ID: 'ABC', Name: 'Magenic'};
    
    let isContainObject = arrayHelper.arrayContainsObject(vendors, abcObject, 'ID');
    

    【讨论】:

    • map、filter、find 更易读,代码也更小,写起来更快
    • 为什么它们比简单循环慢得多?据我所知,它们的时间复杂度与它们的简单循环等效项相同。例如,你的两个代码在我看来都是 O(n)。
    • 上面的方法arrayContainsObject应该是一个你写一次就忘记的库方法。如果您愿意,您实际上可以使用数组函数编写它。从可读性的角度来看,没有什么能比 arrayHelper.arrayContainsObject 更好。
    【解决方案5】:

    我解决这个问题的方法是使用 ES6 并创建一个为我们进行检查的函数。此函数的好处是它可以在整个项目中重复使用,以检查给定 keyvalue 要检查的任何对象数组。

    说够了,让我们看看代码

    数组

    const ceos = [
      {
        name: "Jeff Bezos",
        company: "Amazon"
      }, 
      {
        name: "Mark Zuckerberg",
        company: "Facebook"
      }, 
      {
        name: "Tim Cook",
        company: "Apple"
      }
    ];
    

    功能

    const arrayIncludesInObj = (arr, key, valueToCheck) => {
      return arr.some(value => value[key] === valueToCheck);
    }
    

    通话/使用

    const found = arrayIncludesInObj(ceos, "name", "Tim Cook"); // true
    
    const found = arrayIncludesInObj(ceos, "name", "Tim Bezos"); // false
    

    【讨论】:

    • @SebastianSimon 我已经更新了我的代码。当我写这个答案时,我想要更少的经验。
    【解决方案6】:
    const check = vendors.find((item)=>item.Name==='Magenic')
    
    console.log(check)
    

    试试这个代码。

    如果项目或元素存在,则输出将显示该元素。如果它不存在,则输出将为“未定义”。

    【讨论】:

      【解决方案7】:

      如果我错了,请纠正我.. 我本可以像这样使用forEach 方法,

      var found=false;
      vendors.forEach(function(item){
         if(item.name === "name"){
             found=true;
      
         }
      });
      

      现在我已经习惯了,因为它简单且不言自明。 谢谢。

      【讨论】:

      • 注意:这里没有return
      【解决方案8】:

      可能为时已晚,但 javascript 数组有两个方法 someevery 方法返回一个布尔值,可以帮助您实现这一目标。

      我认为some 最适合您想要实现的目标。

      vendors.some( vendor => vendor['Name'] !== 'Magenic' )
      

      Some 验证数组中的任何对象是否满足给定条件。

      vendors.every( vendor => vendor['Name'] !== 'Magenic' )
      

      Every 验证数组中的所有对象都满足给定条件。

      【讨论】:

      • @ThanwaCh。 - 它应该返回错误!在您的情况下,您需要使用 array.some 方法!
      【解决方案9】:
      const a = [{one:2},{two:2},{two:4}]
      const b = a.filter(val => "two" in val).length;
      if (b) {
         ...
      }
      

      【讨论】:

      • 请提供一些描述并确保您提供的示例有效..(过滤器不会更改原始数组而是克隆它)。
      • 这并不能回答问题。当您撰写此答案时,标题是“如何确定 Javascript 数组是否包含具有等于给定值的属性的对象?”。您正在检查一个对象是否在其原型链上具有属性,而不是对象的属性是否具有特定值。
      • @Sebastian Simon “如果指定的属性在指定的对象或其原​​型链中,则 in 运算符返回 true。”来自developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
      • @user1665355 是的,正确的。我就是这么说的。
      【解决方案10】:

      正如 OP 提出的问题 密钥是否存在

      使用 ES6 reduce 函数返回布尔值的更优雅的解决方案可以是

      const magenicVendorExists =  vendors.reduce((accumulator, vendor) => (accumulator||vendor.Name === "Magenic"), false);
      

      注意:reduce的初始参数是false,如果数组有key则返回true。

      希望它有助于更​​好、更简洁的代码实现

      【讨论】:

      • 从什么时候!![]等于false?
      • 不错的收获。使用 reduce 更新答案:)
      • 这是错误的。 reduce 的第一个参数是累加器,而不是 vendor 对象。这会在每个循环中检查 false.Name === "Magenic" 并返回 false
      • @adiga:已更正。
      • 请检查 Mirza Leka 的解决方案。一个更优雅的解决方案。
      【解决方案11】:

      测试数组元素:

      JS 提供数组函数,让您相对容易地实现这一点。它们是:

      1. Array.prototype.filter: 接受一个回调函数,它是一个测试,然后用 is 回调迭代数组,并根据这个回调过滤。 返回一个新的过滤数组。
      2. Array.prototype.some: 接受一个作为测试的回调函数,然后用 is callback 迭代数组,如果有任何元素通过测试, 返回布尔值 true。否则返回 false

      具体细节最好通过一个例子来解释:

      示例:

      vendors = [
          {
            Name: 'Magenic',
            ID: 'ABC'
           },
          {
            Name: 'Microsoft',
            ID: 'DEF'
          } //and so on goes array... 
      ];
      
      // filter returns a new array, we instantly check if the length 
      // is longer than zero of this newly created array
      if (vendors.filter(company => company.Name === 'Magenic').length ) {
        console.log('I contain Magenic');
      }
      
      // some would be a better option then filter since it directly returns a boolean
      if (vendors.some(company => company.Name === 'Magenic')) {
        console.log('I also contain Magenic');
      }

      浏览器支持:

      这两个函数是ES6 函数,并不是所有的浏览器都支持它们。为了克服这个问题,您可以使用 polyfill。这是 Array.prototype.some 的 polyfill(来自 MDN):

      if (!Array.prototype.some) {
        Array.prototype.some = function(fun, thisArg) {
          'use strict';
      
          if (this == null) {
            throw new TypeError('Array.prototype.some called on null or undefined');
          }
      
          if (typeof fun !== 'function') {
            throw new TypeError();
          }
      
          var t = Object(this);
          var len = t.length >>> 0;
      
          for (var i = 0; i < len; i++) {
            if (i in t && fun.call(thisArg, t[i], i, t)) {
              return true;
            }
          }
      
          return false;
        };
      }

      【讨论】:

        【解决方案12】:

        你可以试试它对我的工作。

        const _ = require('lodash');
        
        var arr = [
          {
            name: 'Jack',
            id: 1
          },
          {
            name: 'Gabriel',
            id: 2
          },
          {
            name: 'John',
            id: 3
          }
        ]
        
        function findValue(arr,value) {
          return _.filter(arr, function (object) {
            return object['name'].toLowerCase().indexOf(value.toLowerCase()) >= 0;
          });
        }
        
        console.log(findValue(arr,'jack'))
        //[ { name: 'Jack', id: 1 } ]
        

        【讨论】:

        • 嗯,这是一个非常古老的问题,我认为它的更新已经有了当今最好的解决方案。
        • 不是每个人都使用 lodash 或想要这种依赖。该语言如何做到最好?
        【解决方案13】:

        为了比较一个对象和另一个对象,我结合了 for in 循环(用于循环对象)和 some()。 您不必担心数组超出范围等,这样可以节省一些代码。 .some 上的文档可以在here

        找到
        var productList = [{id: 'text3'}, {id: 'text2'}, {id: 'text4', product: 'Shampoo'}]; // Example of selected products
        var theDatabaseList = [{id: 'text1'}, {id: 'text2'},{id: 'text3'},{id:'text4', product: 'shampoo'}];    
        var  objectsFound = [];
        
        for(let objectNumber in productList){
            var currentId = productList[objectNumber].id;   
            if (theDatabaseList.some(obj => obj.id === currentId)) {
                // Do what you need to do with the matching value here
                objectsFound.push(currentId);
            }
        }
        console.log(objectsFound);
        

        我比较一个对象和另一个对象的另一种方法是使用带有 Object.keys().length 的嵌套 for 循环来获取数组中对象的数量。代码如下:

        var productList = [{id: 'text3'}, {id: 'text2'}, {id: 'text4', product: 'Shampoo'}]; // Example of selected products
        var theDatabaseList = [{id: 'text1'}, {id: 'text2'},{id: 'text3'},{id:'text4', product: 'shampoo'}];    
        var objectsFound = [];
        
        for(var i = 0; i < Object.keys(productList).length; i++){
                for(var j = 0; j < Object.keys(theDatabaseList).length; j++){
                if(productList[i].id === theDatabaseList[j].id){
                    objectsFound.push(productList[i].id);
                }       
            }
        }
        console.log(objectsFound);
        

        要回答您的确切问题,如果只是在对象中搜索值,您可以使用单个 for in 循环。

        var vendors = [
            {
              Name: 'Magenic',
              ID: 'ABC'
             },
            {
              Name: 'Microsoft',
              ID: 'DEF'
            } 
        ];
        
        for(var ojectNumbers in vendors){
            if(vendors[ojectNumbers].Name === 'Magenic'){
                console.log('object contains Magenic');
            }
        }
        

        【讨论】:

          【解决方案14】:

          2018 年编辑:这个答案来自 2011 年,当时浏览器还没有广泛支持数组过滤方法和箭头函数。看看CAFxX's answer

          没有“神奇”的方法可以在没有循环的情况下检查数组中的某些内容。即使您使用某些函数,该函数本身也会使用循环。您可以做的是在找到所需内容后立即跳出循环,以最大限度地减少计算时间。

          var found = false;
          for(var i = 0; i < vendors.length; i++) {
              if (vendors[i].Name == 'Magenic') {
                  found = true;
                  break;
              }
          }
          

          【讨论】:

          • 没问题。请记住,Keith's solution 也非常可行,可以避免循环。
          • 如果您只需要知道“某物”是否在其中,则不需要标志,您可以使用数组大小​​检查扫描索引的值。为此,索引变量当然需要在 for 语句之前声明。
          • 这些选项现在似乎有效:vendors.forEach、vendors.filter、vendors.reduce
          • 那么 JSON.stringify(vendors).indexOf('Magenic') !== -1
          • @LastBreath 如果 'Magenic' 位于对象中的其他位置,很容易导致误报
          【解决方案15】:

          这就是我的做法

          const found = vendors.some(item => item.Name === 'Magenic');
          

          array.some() 方法检查数组中是否至少有一个值符合条件并返回布尔值。 从这里开始,您可以使用:

          if (found) {
          // do something
          } else {
          // do something else
          }
          

          【讨论】:

            【解决方案16】:

            这里的许多答案都很好而且很简单。但是,如果您的对象数组具有一组固定的值,那么您可以使用以下技巧:

            映射对象中的所有名称。

            vendors = [
                {
                  Name: 'Magenic',
                  ID: 'ABC'
                 },
                {
                  Name: 'Microsoft',
                  ID: 'DEF'
                }
            ];
            
            var dirtyObj = {}
            for(var count=0;count<vendors.length;count++){
               dirtyObj[vendors[count].Name] = true //or assign which gives you true.
            }
            

            现在这个dirtyObj你可以一次又一次地使用而没有任何循环。

            if(dirtyObj[vendor.Name]){
              console.log("Hey! I am available.");
            }
            

            【讨论】:

              【解决方案17】:

              var without2 = (arr, args) => arr.filter(v => v.id !== args.id); 示例:

              without2([{id:1},{id:1},{id:2}],{id:2})

              结果: without2([{id:1},{id:1},{id:2}],{id:2})

              【讨论】:

              • 我想你的意思是说结果:[{id:1},{id:1}]
              【解决方案18】:

              您也可以这样做:

              const find = (key, needle) => return !!~vendors.findIndex(v => (v[key] === needle));
              

              【讨论】:

              • 你最好说他为什么能这样做
              【解决方案19】:

              您可以使用 lodash。如果 lodash 库对您的应用程序来说太重,请考虑分块未使用的不必要函数。

              let newArray = filter(_this.props.ArrayOne, function(item) {
                                  return find(_this.props.ArrayTwo, {"speciesId": item.speciesId});
                              });
              

              这只是一种方法。另一个可以是:

              var newArray=  [];
                   _.filter(ArrayOne, function(item) {
                                      return AllSpecies.forEach(function(cItem){
                                          if (cItem.speciesId == item.speciesId){
                                          newArray.push(item);
                                        }
                                      }) 
                                  });
              

              console.log(arr);

              上面的例子也可以在不使用任何库的情况下重写

              var newArray=  [];
              ArrayOne.filter(function(item) {
                              return ArrayTwo.forEach(function(cItem){
                                  if (cItem.speciesId == item.speciesId){
                                  newArray.push(item);
                                }
                              }) 
                          });
              console.log(arr);
              

              希望我的回答有帮助。

              【讨论】:

                【解决方案20】:

                根据 ECMAScript 6 规范,您可以使用 findIndex

                const magenicIndex = vendors.findIndex(vendor =&gt; vendor.Name === 'Magenic');

                magenicIndex 将保存0(数组中的索引)或-1(如果未找到)。

                【讨论】:

                • 只是为了让人们知道,如果将 0 用作条件,仍然会匹配为错误结果。出于这个原因,我认为 find() 会更好,因为您可以获得更逻辑真实的评估。
                • 不仅是@dhj 提到的内容,而且可能会在以后的索引中找到(1、2 等)。因此,您需要检查索引是否至少为 0,因此大多数直接产生布尔可用值的解决方案会更加优雅。
                【解决方案21】:

                不需要循环。想到的三种方法:

                Array.prototype.some()

                这是您问题的最准确答案,即“检查是否存在”,暗示布尔结果。如果有任何 'Magenic' 对象,则为 true,否则为 false:

                let hasMagenicVendor = vendors.some( vendor => vendor['Name'] === 'Magenic' )
                

                Array.prototype.filter()

                这将返回一个包含所有“Magenic”对象的数组,即使只有一个(将返回一个单元素数组):

                let magenicVendors = vendors.filter( vendor => vendor['Name'] === 'Magenic' )
                

                如果您尝试将其强制为布尔值,它将不起作用,因为空数组(没有“Magenic”对象)仍然是真实的。因此,只需在条件中使用 magenicVendors.length

                Array.prototype.find()

                这将返回第一个“Magenic”对象(或undefined,如果没有):

                let magenicVendor = vendors.find( vendor => vendor['Name'] === 'Magenic' );
                

                这会强制转换为布尔值 OK(任何对象都是真实的,undefined 是虚假的)。


                注意:我使用 vendor["Name"] 而不是 vendor.Name,因为属性名称的大小写很奇怪。

                注意 2:检查名称时没有理由使用松散相等 (==) 而不是严格相等 (===)。

                【讨论】:

                • 有必要指出,在后台,这些都是循环的。这些在计算上也都比简单的循环和执行操作要慢。
                • 不妨在这里分享这份爱:stackoverflow.com/questions/21748670/…,这样更多像我这样的人就不会导航到那个旧页面并做出假设。
                【解决方案22】:

                我宁愿使用正则表达式。

                如果你的代码如下,

                vendors = [
                    {
                      Name: 'Magenic',
                      ID: 'ABC'
                     },
                    {
                      Name: 'Microsoft',
                      ID: 'DEF'
                    }
                ];
                

                我会推荐

                /"Name":"Magenic"/.test(JSON.stringify(vendors))
                

                【讨论】:

                • 有些人在遇到问题时会想“我知道,我会使用正则表达式”。现在他们有两个问题。
                • 把这个归档,仅仅因为你可以做某事,并不意味着你应该这样做。
                • 撇开恶作剧和幽默。有几种简单的对象和数组访问以及迭代方法和表达式。为什么正则表达式会成为您的选择?问题是如何确定一个数组是否包含一个对象,该对象的属性值与"Magenic" 匹配。正则表达式答案的误报:[ { "Not the property you’re looking for": { "Name": "Magenic" } } ][ { 'Not the property you’re looking for"Name': "Magenic" } ];潜在的假阴性(如果问题要求自己的属性,特别是):[ Object.create({ Name: "Magenic" }) ].
                【解决方案23】:

                如果你使用 jquery,你可以利用 grep 来创建包含所有匹配对象的数组:

                var results = $.grep(vendors, function (e) {
                    return e.Name == "Magenic";
                });
                

                然后使用结果数组:

                for (var i=0, l=results.length; i<l; i++) {
                    console.log(results[i].ID);
                }
                

                【讨论】:

                  【解决方案24】:

                  你必须循环,没有办法绕过它。

                  function seekVendor(vendors, name) {
                    for (var i=0, l=vendors.length; i<l; i++) {
                      if (typeof vendors[i] == "object" && vendors[i].Name === name) {
                        return vendors[i];
                      }
                    }
                  }
                  

                  当然,您可以使用像 linq.js 这样的库来使这更令人愉悦:

                  Enumerable.From(vendors).Where("$.Name == 'Magenic'").First();
                  

                  (请参阅jsFiddle 以获取演示)

                  我怀疑 linq.js 会比直接循环更快,但当事情变得更复杂时,它肯定会更灵活。

                  【讨论】:

                    【解决方案25】:

                    你不能不仔细观察对象。

                    你可能应该稍微改变一下你的结构,比如

                    vendors = {
                        Magenic:   'ABC',
                        Microsoft: 'DEF'
                    };
                    

                    然后你可以像查找哈希一样使用它。

                    vendors['Microsoft']; // 'DEF'
                    vendors['Apple']; // undefined
                    

                    【讨论】:

                      【解决方案26】:

                      除非你想像这样重组它:

                      vendors = {
                          Magenic: {
                            Name: 'Magenic',
                            ID: 'ABC'
                           },
                          Microsoft: {
                            Name: 'Microsoft',
                            ID: 'DEF'
                          } and so on... 
                      };
                      

                      你可以做if(vendors.Magnetic)

                      你必须循环

                      【讨论】:

                      • 如果他仍然想维护对象结构以便在其他地方使用它
                      • 你会怎么做?
                      猜你喜欢
                      • 1970-01-01
                      • 2016-04-21
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2017-02-08
                      相关资源
                      最近更新 更多