过滤n个数组,排除不存在于每个数组中的值

3

我有n个数组,需要确定x是否在所有的n个数组中。(其中n是任何数字,而x是一个数值)我已经尝试了以下代码,但结果总是错误的。

function filterArrays()
{
  var x = $(this).attr('id'); // ex: 2
  var arrays = [[1,2,3],[2,4,6]];
  var result = false;
  for each (var n in arrays)
  {
    result = result ^ (n.indexOf(x) > -1);
  }
}

x同时在两个数组中时,如何使result等于true,但当x不在这两个数组中时,使result等于false

上述函数将与jQuery的filter()方法一起使用。例如:

$(arrayOfElementsWithNumericIds).filter(arrayFilter);
// arrayOfElementsWithNumericIds prototype: [div#1,div#2,div#3,...]

我认为需要进行位运算,但我可能错了。请解释您的解决方案为什么是正确的,以及为什么我的解决方案不起作用。(如果能提供额外解释就更好了)
6个回答

3

以下是您示例的一些问题:

  • 将数字与字符串进行比较(id是字符串)。请使用x = parseInt(...)
  • 使用^运算符。而是将result初始化为true并使用&&
  • 摆脱each。正确的语法是for (key in object)

我尽可能少地修改了您的代码:

function filterArrays()
{
    var x = parseInt($(this).attr('id')); // ex: 2
    var arrays = [[1,2,3],[2,4,6]];
    var result = true;
    for (var n in arrays)
    {
        result = result && (arrays[n].indexOf(x) > -1);
    }
    return result;
}

话虽如此,你可以通过使用Array.every()Array.some()来优化代码。此外,使用$(this).attr('id')会不必要地创建一个jQuery对象,因为你可以直接说this.id
function filterArrays()
{
    var x = parseInt(this.id); // ex: 2
    var arrays = [[1,2,3],[2,4,6]];
    var result = arrays.every(function(array)
    {
        return array.some(function(item)
        {
            return item === x;
        });
    });
    return result;
}

感谢您对我如何改进代码的建议。虽然我不同意您在我的示例中使用量词“很多”来描述错误,但我感谢您指出了您发现的“少数”错误,并解释了它们如何/为什么可以改进。您的答案最为详尽,因此我将接受它。感谢您的努力。 - sholsinger
@sholsinger:谢谢。我并没有“很多”(lots)的意思......你是对的,“几个”(few)更精确。 :-) - gilly3
我的实际设置不同。在我的真实代码中,“arrays”是一个对象,因此不支持“every()”。有没有一种方法可以将对象转换为数组而不会产生太多开销? - sholsinger
1
@sholsinger:你可以这样做,但它不会给你带来太多好处,如果有的话。对于迭代对象,使用已经存在的for ... in应该足够了。我唯一要添加的是,在循环中加入一个break语句,如果result为false。 - gilly3

1

我认为这就是你要找的:

  var result = true;
 for each (var n in arrays)
  {
    result = result && (n.indexOf(x) > -1);
  }

假设开始时所有数组中的值都存在。然后使用AND(&&)运算符即可获得该值。
  true AND (value is in current array)

如果任何时候该值不在数组中,它将变为 false,并且整个操作将变为 false。否则,它将保持为 true 直到循环结束。


谢谢,你可能已经知道了,但是你的答案是正确的,并且可以修复我提供的示例代码。我很感激你花费时间来制作你的答案。 - sholsinger

1

xor不是解决问题的方法。可以这样看待:

search for 2, start  result = false
1st array: 2 is present, result = false xor true = true
2nd array: 2 is present, result = true xor true = false
end: result is false (WRONG)

search for 4, start result = false
1st array: 4 is present, result = false xor true = true
2nd array: 4 is  absent, result = true xor false = true
end: result is true (WRONG)

你想要一个累积的位与操作。
start: result = true, search for 2
1st array: 2 is present, result = true and true = true
2nd array: 2 is present, result = true and true = true
end: result is true (RIGHT)

start: result = true, search for 4
1st array: 4 is present, result = true and true = true
2nd array: 4 is absent, result = true and false = false
end: result if false (RIGHT)

感谢您对从XOR到AND的更改提出的建议。那是技术上正确的,可以解决问题。(大部分)感谢您的努力。 - sholsinger

0

如果你想的话,可以循环遍历你的 'arrays' 对象,但我认为你只是想要一个集合交集操作。这是在jQuery中实现它的一种方式,它不会关心x的attr(id)值是整数还是字符串。我正在吃午饭,稍后会快速测试一下...

function filterArrays(){
  var x = $(this).attr("id");
  var arrays = [[1,2,3],[2,4,6]];
  var result = ($.inArray(arrays[0], x )>0 && $.inArray(arrays[1], x) >0);
  return result;
}

@marc b 我同意,累积按位与操作。据我所知,我的帖子是一个n^2的解决方案。 - DefyGravity
查看我的答案中预先编写的代码,了解两种不同的 Set 实现。 - Phrogz

0

使用http://phrogz.net/JS/ArraySetMath.js,您可以:

var sets  = [[1,2,3],[2,3,7],[1,7,2]];
var isect = sets[0];
for (var i=1,len=sets.length;i<len;++i){
  isect = isect.intersection( sets[i] );
}
console.log( isect );
// [2]

或者,使用JS.Set,你可以:

var sets  = [[1,2,3],[2,3,7],[1,7,2]];

// Or JS.HashSet or JS.Set
var isect = new JS.SortedSet(sets[0]);
for (var i=1,len=sets.length;i<len;++i){
  isect = isect.intersection( new JS.SortedSet(sets[i]) );
}

谢谢,我很感激你为制作那个库所付出的努力。话虽如此,我现在不想给我的脚本增加任何额外的开销。 - sholsinger

0
为什么不使用包含方法扩展数组原型?这样你就可以循环遍历每个数组,或者/和将当前结果与上一个结果合并。

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