如何在Javascript中区分关联数组和普通数组?

3
在Javascript中,是否可以获取关联数组和普通数组之间的差异?
例如:
array1 = [5, 1, 3];
array2 = [1 => 15, 2 => 20, 3 => 10, 4 => 5, 5 =>50 ];

差别在于...
diff = [2 => 20, 4=> 5];

5
你的第二个数组应该是一个对象,而不是一个数组(JavaScript实际上没有关联数组)。将以上内容粘贴到Chrome的JavaScript控制台中会报告:"SyntaxError: Unexpected token >"。此外,可能会有重复,参见:https://dev59.com/aXM_5IYBdhLWcg3w3nbz。 - David Thomas
你的第二个数组不是有效的JavaScript。 - Brandon Boone
你的意思是第二个数组应该像这样... array2[1] = 15 吗? - JohnSmith
如果我理解正确,diff 应该只提供那些包含在 array2 中但不在 array1 中的 _值_。然而,array2 具有值 15、20、10、5、50,因此差异应该是 [15、20、10、50],因为 1 和 3 只是 array2 的 _键_。 虽然在 JS 对象符号和 Perl 哈希符号之间混淆很棒,但当我在短时间内同时进行两者或来回切换时,这种情况总会发生。 - yogibimbi
4个回答

4

我猜您的问题只是在声明array2时有个小错误,这不是什么大问题。

无论如何,下面是一个小技巧,但它可以给您想要的结果:

array1 = [5, 1, 3];
array2 = {1: 15, 2: 20, 3: 10, 4: 5, 5: 50};

var result = {};
for (var i in array2) {
    if (array1.indexOf(+i) < 0) {
        result[i] = array2[i];
    }
}

alert(JSON.stringify(result));

工作示例

我的技巧是在indexOf调用中使用+i,因为你的“关联数组”的属性是字符串,但是你的array1包含数字。一元运算符+可以从字符串中生成一个数字。这有点儿取巧,但它是惯用的和被接受的JavaScript实践方法。

附录

正如RobG所指出的那样,indexOf是一个ES5方法,所以如果你的JavaScript引擎是ES3或以下版本,则需要自己实现indexOfMDN上提供了一个实现示例。或者,您可以通过自己搜索数组来实现与indexOf等效的操作。


没有ES5 indexOf方法的UA(例如IE 8及更低版本)将抛出错误。 - RobG
感谢 @RobG,已向答案中添加了 ES5 的说明。 - Ray Toal

3

首先,你的第二个数组应该是一个对象,并且不是有效的JavaScript,就像前两个评论者说的那样。以下是以对象形式呈现:

var object = { "1": 15, "2": 20, "3": 10, "4": 5, "5": 50 };

这个函数可以实现你所需的结果:
function findDiff(arr, obj)
{
    var tempObj = clone(obj);
    for (var i = 0; i < arr.length; i++)
    {
        var propId = arr[i];
        if (tempObj[propId] !== undefined)
            delete tempObj[propId];
    }
    return tempObj;
}

这个函数依赖于一个叫做clone的函数,它确保通过值而不是引用将obj复制到tempObj中。这可以防止传递的对象被修改:

function clone(obj){
    if(obj == null || typeof(obj) != 'object')
        return obj;

    var temp = obj.constructor(); // changed

    for(var key in obj)
        temp[key] = clone(obj[key]);
    return temp;
}

只需这样调用:
var array = [5, 1, 3];
var object = { "1": 15, "2": 20, "3": 10, "4": 5, "5": 50 };

var diff = findDiff(array, object);

1
请注意,tempObj = obj 的赋值实际上没有任何有用的作用,该函数返回修改后的原始对象,而不是副本。此外,在尝试删除属性名称之前,应该检查它(参见ES5 delete operator),因为在严格模式下可能会抛出错误。 - RobG
你说得对,发现得好(特别是关于我的浅拷贝,我不知道我怎么会漏掉了)。已编辑我的答案。 - jeff

2
你需要详细解释操作的预期结果。
一个解释是基于另一个数组中的值从一个数组中移除成员,因此给定:
array1 = [5, 1, 3];
array2 = [15, 20, 10, 5, 50 ];

您可能有这样一个函数:

function diff(arr1, arr2) {
  // Copy arrays so don't affect originals
  var t1 = arr1.slice();
  var t2 = arr2.slice();

  // sort t1
  t1.sort();

  // Remove members of arr2 from highest indexes to lowest
  var i = t1.length;
  while (i--) {
    t2.splice(t1[i], 1);
  }
  return t2;
} 

 alert(diff(array1, array2));  // 15, 10, 50

请注意,数组是从零开始索引的,而array2中没有第5个成员,因此删除的值为20和5(即分别具有索引1和3的成员)。

0
另一个选择是,在您的变量上执行JSON.stringify(),并检查结果字符串的最左侧或最右侧字符。如果它是{},则您有一个关联数组/对象;如果是[],则它是一个数组。我想这可能会有点昂贵,取决于您的数组大小,但它可以完成工作。

然而...不存在免费的午餐,所有的动物都是成对出现的,所以这个解决方案只有在你根据原始定义一致的使用变量时才是合理的。
如果你定义了一个对象,如下所示:obj = [1,2,3],然后,在以后的某个时间点上,添加了一个值,如:obj['w'] = 'ww'JSON.stringify(obj)将只会产生[1,2,3],但如果你再次调用obj['w'],你仍然会得到ww,这意味着这个值并没有被遗忘,它们只是不混合在一起。我想,在内部,JavaScript 保留了两个独立的变量表,一个用于对象,一个用于数组,无论哪个先定义,当用 stringify 打印时就会得到优先。

为了更加混淆事情,如果你先将变量定义为对象,但随后添加了一个数组键,也就是说

obj = {1: 11, 2: 22, 3: 33};
obj[4] = 44;

新的键自动转换为字符串,因此在stringify中产生了一个结果。
{"1":11, "2": 22, "3": 33, "4": 44}  //sans the \", for better readibility

虽然有点混乱,但我想这就是为了一点无政府状态和易用性所付出的代价。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接