Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
344 views
in Technique[技术] by (71.8m points)

javascript - Vanilla JavaScript:如何搜索对象数组,键是数组(Vanilla JavaScript: How to search an array of objects , key is an array)

I have aan array of objects.

(我有一个对象数组。)

Each object is {key, value} For each object key is an array consisting of strings ['foo', 'bar', 'baz'] .

(每个对象都是{key, value}每个对象键都是一个由字符串['foo', 'bar', 'baz']组成的数组。)

I want to remove any duplicates of ['foo', 'bar', 'baz'] from the array so that further processing is only done on unique values.

(我想从数组中删除['foo', 'bar', 'baz']所有重复项,以便仅对唯一值进行进一步处理。)

Currently I'm doing this

(目前我正在这样做)

function removeArrayDupes(input){
var i = input.length;
var previous;
input.sort();

 if (i){
        while (--i){
            var cur = input[i];
            if (!cur.key){
                // Remove empty records
                input.splice(i,1);
            } else {
                // Clear duplicates
                if ((previous) && (input[i].key) === previous){
                    input.splice(i,1);
                    regex= "/" + JSON.stringify(input[i].key) + "/g";
                    previous = input[i].key;
                } else {
                    previous = input[i].key;
                }
            }
        }
    }
}

My problem: If dupes < 3, the single duplicate is not removed.

(我的问题:如果重复数<3,则不会删除单个重复项。)

My brain is tired because I can't see where I'm screwing this up.

(我的大脑很累,因为我看不到我在哪里搞砸。)

I could use an extra brain or two to resolve this.

(我可以多花一两个大脑来解决这个问题。)

I looking for solutions usinng Vanilla JavaScript.

(我正在寻找使用Vanilla JavaScript的解决方案。)

It's not just about making it work< I want to gain comprehension on the problem and if there is a better algorith.

(这不仅仅是使它起作用。我想对这个问题以及是否有更好的算法有所了解。)

TIA

(TIA)

Sample data:

(样本数据:)

[
{key: ["foo","bar","baz"], value: "Some data"},
{key: ["foo","bar","baz"], value: "Some data"},
{key: ["foo","bar","baz"], value: "Some data"},
{key: ["baz","bar","foo"], value: "Some data"},
{key: ["bar","foo","baz"], value: "Some data"},
{key: ["bar","foo","bar"], value: "Some data"},
{key: ["bar","foo","bar"], value: "Some data"},
{key: ["bar","foo","bar"], value: "Some data"},
{key: ["bar","foo","bar"], value: "Some data"},
{key: ["baz","bar","foo"], value: "Some data"},
{key: ["baz","bar","foo"], value: "Some data"},
]

Desired output:

(所需的输出:)

[
{key: ["foo","bar","baz"], value: "Some data"},
{key: ["baz","bar","foo"], value: "Some data"},
{key: ["bar","foo","baz"], value: "Some data"},
{key: ["bar","foo","bar"], value: "Some data"},
{key: ["baz","bar","foo"], value: "Some data"}
]
  ask by Ken Ingram translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

TL;DR(TL; DR)

Using the Set constructor and the spread syntax :

(使用Set构造函数和spread语法 :)

uniq = [...new Set(array)];

"Smart" but na?ve way(“聪明”但幼稚的方式)

uniqueArray = a.filter(function(item, pos) {
    return a.indexOf(item) == pos;
})

Basically, we iterate over the array and, for each element, check if the first position of this element in the array is equal to the current position.

(基本上,我们遍历数组,并针对每个元素检查此元素在数组中的第一个位置是否等于当前位置。)

Obviously, these two positions are different for duplicate elements.

(显然,对于重复元素,这两个位置是不同的。)

Using the 3rd ("this array") parameter of the filter callback we can avoid a closure of the array variable:

(使用过滤器回调的第3个(“此数组”)参数,我们可以避免关闭数组变量:)

uniqueArray = a.filter(function(item, pos, self) {
    return self.indexOf(item) == pos;
})

Although concise, this algorithm is not particularly efficient for large arrays (quadratic time).

(尽管简洁,但是该算法对于大型数组(二次时间)并不是特别有效。)

Hashtables to the rescue(救援的哈希表)

function uniq(a) {
    var seen = {};
    return a.filter(function(item) {
        return seen.hasOwnProperty(item) ? false : (seen[item] = true);
    });
}

This is how it's usually done.

(通常是这样的。)

The idea is to place each element in a hashtable and then check for its presence instantly.

(想法是将每个元素放在哈希表中,然后立即检查其是否存在。)

This gives us linear time, but has at least two drawbacks:

(这给了我们线性的时间,但是至少有两个缺点:)

  • since hash keys can only be strings in JavaScript, this code doesn't distinguish numbers and "numeric strings".

    (由于哈希键只能是JavaScript中的字符串,因此此代码无法区分数字和“数字字符串”。)

    That is, uniq([1,"1"]) will return just [1]

    (也就是说, uniq([1,"1"])将只返回[1])

  • for the same reason, all objects will be considered equal: uniq([{foo:1},{foo:2}]) will return just [{foo:1}] .

    (出于相同的原因,所有对象都将被视为相等: uniq([{foo:1},{foo:2}])仅返回[{foo:1}] 。)

That said, if your arrays contain only primitives and you don't care about types (eg it's always numbers), this solution is optimal.

(就是说,如果您的数组仅包含基元并且您不关心类型(例如,它始终是数字),则此解决方案是最佳的。)

The best from two worlds(来自两个世界的最好)

A universal solution combines both approaches: it uses hash lookups for primitives and linear search for objects.

(通用解决方案结合了这两种方法:它使用哈希查找原始图元和线性搜索对象。)

function uniq(a) {
    var prims = {"boolean":{}, "number":{}, "string":{}}, objs = [];

    return a.filter(function(item) {
        var type = typeof item;
        if(type in prims)
            return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true);
        else
            return objs.indexOf(item) >= 0 ? false : objs.push(item);
    });
}

sort |(排序|) uniq(优衣库)

Another option is to sort the array first, and then remove each element equal to the preceding one:

(另一种选择是先对数组排序,然后删除等于前一个元素的每个元素:)

function uniq(a) {
    return a.sort().filter(function(item, pos, ary) {
        return !pos || item != ary[pos - 1];
    })
}

Again, this doesn't work with objects (because all objects are equal for sort ).

(同样,这不适用于对象(因为所有对象的sort相等)。)

Additionally, we silently change the original array as a side effect - not good!

(另外,我们无声地更改了原始数组作为副作用-不好!)

However, if your input is already sorted, this is the way to go (just remove sort from the above).

(但是,如果您的输入已经排序,这就是方法(只需从上面删除sort )。)

Unique by...(独一无二...)

Sometimes it's desired to uniquify a list based on some criteria other than just equality, for example, to filter out objects that are different, but share some property.

(有时,我们希望根据某些条件(不仅仅是相等性)来对列表进行唯一化,例如,过滤出不同但共享某些属性的对象。)

This can be done elegantly by passing a callback.

(可以通过传递回调来优雅地完成此操作。)

This "key" callback is applied to each element, and elements with equal "keys" are removed.

(此“键”回调将应用于每个元素,并且删除具有相等“键”的元素。)

Since key is expected to return a primitive, hash table will work fine here:

(由于预计key将返回原语,因此哈希表在这里可以正常工作:)

function uniqBy(a, key) {
    var seen = {};
    return a.filter(function(item) {
        var k = key(item);
        return seen.hasOwnProperty(k) ? false : (seen[k] = true);
    })
}

A particularly useful key() is JSON.stringify which will remove objects that are physically different, but "look" the same:

(JSON.stringify是一个特别有用的key() ,它将删除物理上不同但“看起来”相同的对象:)

a = [[1,2,3], [4,5,6], [1,2,3]]
b = uniqBy(a, JSON.stringify)
console.log(b) // [[1,2,3], [4,5,6]]

If the key is not primitive, you have to resort to the linear search:

(如果key不是原始key则必须诉诸线性搜索:)

function uniqBy(a, key) {
    var index = [];
    return a.filter(function (item) {
        var k = key(item);
        return index.indexOf(k) >= 0 ? false : index.push(k);
    });
}

In ES6 you can use a Set :

(在ES6中,您可以使用Set :)

function uniqBy(a, key) {
    let seen = new Set();
    return a.filter(item => {
        let k = key(item);
        return seen.has(k) ? false : seen.add(k);
    });
}

or a Map :

(或Map :)

function uniqBy(a, key) {
    return [
        ...new Map(
            a.map(x => [key(x), x])
        ).values()
    ]
}

which both also work with non-primitive keys.

(两者也都可以与非原始键一起使用。)

First or last?(首先还是最后?)

When removing objects by a key, you might to want to keep the first of "equal" objects or the last one.

(通过键删除对象时,您可能想保留“相等”对象中的第一个或最后一个。)

Use the Set variant above to keep the first, and the Map to keep the last:

(使用上面的Set变量保留第一个变量,使用Map保留最后一个变量:)

 function uniqByKeepFirst(a, key) { let seen = new Set(); return a.filter(item => { let k = key(item); return seen.has(k) ? false : seen.add(k); }); } function uniqByKeepLast(a, key) { return [ ...new Map( a.map(x => [key(x), x]) ).values() ] } // data = [ {a:1, u:1}, {a:2, u:2}, {a:3, u:3}, {a:4, u:1}, {a:5, u:2}, {a:6, u:3}, ]; console.log(uniqByKeepFirst(data, it => it.u)) console.log(uniqByKeepLast(data, it => it.u)) 

Libraries(图书馆)

Both underscore and Lo-Dash provide uniq methods.

(下划线Lo-Dash均提供uniq方法。)

Their algorithms are basically similar to the first snippet above and boil down to this:

(他们的算法基本上类似于上面的第一个代码片段,归结为:)

var result = [];
a.forEach(function(item) {
     if(result.indexOf(item) < 0) {
         result.push(item);
     }
});

This is quadratic, but there are nice additional goodies, like wrapping native indexOf , ability to uniqify by a key ( iteratee in their parlance), and optimizations for already sorted arrays.

(这是二次方的,但是还有很多其他好处,例如包装本机indexOf ,按键进行唯一化的能力(用术语iteratee表示)以及对已排序数组的优化。)

If you're using jQuery and can't stand anything without a dollar before it, it goes like this:

(如果您使用的是jQuery,但在没有美元之前不能忍受任何事情,它会像这样:)

  $.uniqArray = function(a) {
        return $.grep(a, function(item, pos) {
            return $.inArray(item, a) === pos;
        });
  }

which is, again, a variation of the first snippet.

(再次是第一个代码段的变体。)

Performance(性能)

Function calls are expensive in JavaScript, therefore the above solutions, as concise as they are, are not particularly efficient.

(函数调用在JavaScript中非常昂贵,因此上述解决方案尽管非常简洁,但并不是特别有效。)

For maximal performance, replace filter with a loop and get rid of other function calls:

(为了获得最佳性能,请用循环替换filter并摆脱其他函数调用:)

function uniq_fast(a) {
    var seen = {};
    var out = [];
    var len = a.length;
    var j = 0;
    for(var i = 0; i < len; i++) {
         var item = a[i];
         if(seen[item] !== 1) {
               seen[item] = 1;
               out[j++] = item;
         }
    }
    return out;
}

This chunk of ugly code does the same as the snippet #3 above, but an order of magnitude faster (as of 2017 it's only twice as fast - JS core folks are doing a great job!)

(这段丑陋的代码与上面的代码段#3一样, 但是速度提高了一个数量级 (截至2017年,它的速度仅是后者的两倍-JS核心人员做得很好!))

 function uniq(a) { var seen = {}; return a.filter(function(item) { return seen.hasOwnProperty(item) ? false : (seen[item] = true); }); } function uniq_fast(a) { var seen = {}; var out = []; var len = a.length; var j = 0; for(var i = 0; i < len; i++) { var item = a[i]; if(seen[item] !== 1) { seen[item] = 1; out[j++] = item; } } return out; } ///// var r = [0,1,2,3,4,5,6,7,8,9], a = [], LEN = 1000, LOOPS = 1000; while(LEN--) a = a.concat(r); var d = new Date(); for(var i = 0; i < LOOPS; i++) uniq(a); document.write('<br>uniq, ms/loop: ' + (new Date() - d)/LOOPS) var d = new Date(); for(var i = 0; i < LOOPS; i++) 

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...