为啥 Array.indexOf 找不到外观相同的对象

Posted

技术标签:

【中文标题】为啥 Array.indexOf 找不到外观相同的对象【英文标题】:Why Array.indexOf doesn't find identical looking objects为什么 Array.indexOf 找不到外观相同的对象 【发布时间】:2012-09-18 05:12:04 【问题描述】:

我有一个包含对象的数组。

类似这样的:

var arr = new Array(
  x:1, y:2,
  x:3, y:4
);

当我尝试时:

arr.indexOf(x:1, y:2);

它返回-1

如果我有字符串或数字或其他类型的元素但对象,那么indexOf() 可以正常工作。

有谁知道为什么以及我应该如何在数组中搜索对象元素?

当然,我的意思是除了为对象创建字符串哈希键并将其提供给数组之外的方法......

【问题讨论】:

我想知道为什么不能选择散列? 请注意,根据定义,两个对象永远不会相等,即使它们具有完全相同的属性名称和值。 objectA === objectB 当且仅当 objectAobjectB 引用同一个对象。 规范没有对字符串这么说:如果它们都是字符串并且具有相同的字符,则它们是相等的。 我同意 === 比较器,但为什么它不适用于 == 这个比较器我不明白... @dystroy—如果您使用字符串对象(并且 OP 正在谈论对象),它们永远不会相等,因为它们是类型对象,而不是字符串。 【参考方案1】:

由于没有人提到内置函数 Array.prototype.findIndex(),我想提一下,它完全符合作者的需要。

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

var array1 = [5, 12, 8, 130, 44];

function findFirstLargeNumber(element) 
  return element > 13;


console.log(array1.findIndex(findFirstLargeNumber));
// expected output: 3

在你的情况下是:

arr.findIndex(function(element) 
 return element.x == 1 && element.y == 2;
);

或者使用 ES6

arr.findIndex( element => element.x == 1 && element.y == 2 );

以上示例的更多信息:https://developer.mozilla.org/en-US/docs/Web/javascript/Reference/Global_Objects/Array/findIndex

【讨论】:

最佳答案,无需编写新函数!【参考方案2】:

因为两个独立的对象彼此不是===,而indexOf 使用===。 (他们也不是==。)

例子:

var a = x:1, y:2;
var b = x:1, y:2;
console.log(a === b);

===== 测试它们的操作数是否引用相同的对象,而不是它们是否引用等效对象(具有相同原型和属性的对象)。

【讨论】:

【参考方案3】:

看起来您对这种类型的答案不感兴趣,但对于感兴趣的其他人来说,这是最简单的:

var arr = new Array(
    x:1, y:2,
    x:3, y:4
);

arr.map(function(obj) 
    return objStr(obj);
).indexOf(objStr(x:1, y:2));

function objStr(obj) 
    return "(" + obj.x + ", " + obj.y + ")"

【讨论】:

【参考方案4】:

无需自定义代码即可使用

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

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

【讨论】:

恭喜,你还是比某些库快。【参考方案5】:

这是另一种解决方案,您将比较函数作为参数传递:

function indexOf(array, val, from, compare) 

  if (!compare) 
    if (from instanceof Function) 
      compare = from;
      from = 0;
    
    else return array.__origIndexOf(val, from);
  

  if (!from) from = 0;

  for (var i=from ; i < array.length ; i++) 
    if (compare(array[i], val))
      return i;
  
  return -1;


// Save original indexOf to keep the original behaviour
Array.prototype.__origIndexOf = Array.prototype.indexOf;

// Redefine the Array.indexOf to support a compare function.
Array.prototype.indexOf = function(val, from, compare) 
  return indexOf(this, val, from, compare);

然后您可以通过以下方式使用它:

indexOf(arr, x:1, y:2, function (a,b) 
 return a.x == b.x && a.y == b.y;
);

arr.indexOf(x:1, y:2, function (a,b) 
 return a.x == b.x && a.y == b.y;
);

arr.indexOf(x:1, y:2, 1, function (a,b) 
 return a.x == b.x && a.y == b.y;
);

好在如果没有传递比较函数,它仍然会调用原始的 indexOf。

[1,2,3,4].indexOf(3);

【讨论】:

【参考方案6】:

如前所述,两个对象永远不会相等,但如果它们指向同一个对象,则引用可以相等,因此要使代码执行您想要的操作:

var a = x:1, y:2;
var b = x:3, y:4;
var arr = [a, b];

alert(arr.indexOf(a)); // 0

编辑

这是一个更通用的 specialIndexOf 函数。请注意,它期望对象的值是原始的,否则需要更严格。

function specialIndexOf(arr, value) 
  var a;
  for (var i=0, iLen=arr.length; i<iLen; i++) 
    a = arr[i];

    if (a === value) return i;

    if (typeof a == 'object') 
      if (compareObj(arr[i], value)) 
        return i;
      
     else 
      // deal with other types
    
  
  return -1;

  // Extremely simple function, expects the values of all 
  // enumerable properties of both objects to be primitives.
  function compareObj(o1, o2, cease) 
    var p;

    if (typeof o1 == 'object' && typeof o2 == 'object') 

      for (p in o1) 
        if (o1[p] != o2[p]) return false; 
      

      if (cease !== true) 
        compareObj(o2, o1, true);
      

      return true;
    
  


var a = new String('fred');
var b = new String('fred');

var arr = [0,1,a];

alert(specialIndexOf(arr, b)); // 2

【讨论】:

是的,但我动态生成我搜索的对象。谢谢。 然后您将不得不遍历属性并比较值。您还必须比较两种方式,即 a to be 和 b to a. 为什么要双向?我这样写函数:availableMoves.indexOf = function(obj) for(i in this) if (this[i].x == obj.x && this[i].y == obj.y) return parseInt(i); 返回 -1; 如果您有这些属性名称,那将起作用。顺便说一句,不建议在数组上使用 for..in,尤其是在“猴子补丁”为 Array.prototpye 添加 ES5 特性以添加可枚举属性的流行情况下。使用带有数字索引的普通 for 循环,然后您可以只返回 i 而无需将其转换为数字。【参考方案7】:

indexOf 使用严格相等将 searchElement 与 Array 的元素进行比较(与 === 或三等号运算符使用的方法相同)。

您不能使用=== 来检查对象的等价性。

正如@RobG 指出的那样

请注意,根据定义,两个对象永远不会相等,即使它们具有完全相同的属性名称和值。 objectA === objectB 当且仅当 objectA 和 objectB 引用同一个对象。

您可以简单地编写一个自定义 indexOf 函数来检查对象。

function myIndexOf(o)     
    for (var i = 0; i < arr.length; i++) 
        if (arr[i].x == o.x && arr[i].y == o.y) 
            return i;
        
    
    return -1;

演示: http://jsfiddle.net/zQtML/

【讨论】:

这意味着,我无法比较两个对象是否相同? 您可以使用=== 来检查对象的相等性,当这两个对象是完全不同的文字时,它会失败。 @Jibla 编写一个简单的函数来遍历对象并找到匹配项。检查更新的帖子。 @Vega - 是的,我知道编写自定义函数的选项最少,我只是不想编写自定义函数。附言谢谢! @jbaby——两个对象永远不会相等,无论您使用== 还是===。如果要比较的表达式是对象,abstract 和 strict 相等比较算法(以及关系比较算法)都定义为返回 false。【参考方案8】:

那些对象不相等。

您必须实现自己的功能。

例如,您可以这样做:

var index = -1;
arr.forEach(function(v, i) 
   if (this.x==v.x && this.y==v.y) index=i;
, searched); 

searched 是您的对象之一(或不是)。

(我会用一个简单的循环来实现它,但用 foreach 更漂亮)

【讨论】:

以上是关于为啥 Array.indexOf 找不到外观相同的对象的主要内容,如果未能解决你的问题,请参考以下文章

为啥我在操作菜单上找不到“调试托管内存”?

在单元测试中模拟时找不到 Laravel 外观类

为啥我下载的mysql都找不到SETUP.EXE安装文件呢?无论啥版本都没有

intellij IDEA为啥我代码编译的时候没有问题 但是maven打包的时候报错找不到类?代码中都import过了啊

总是提示找不到符号为啥

为啥找不到 getservletcontext?