简体   繁体   English

从Javascript数组中删除等效但唯一的对象

[英]Removing equivalent but unique objects from a Javascript array

I have an array of objects similar to the following: 我有一个类似于以下对象的数组:

var routeArr = [
    {start: 1, end: 2},
    {start: 1, end: 3},
    {start: 1, end: 4},
    {start: 2, end: 1},
    {start: 3, end: 1},
    {start: 4, end: 1}
];

These objects represent the start and end point of lines and as such, {start: 1, end: 2} and {start: 2, end: 1} represent the same line. 这些对象表示行的起点和终点,因此{start: 1, end: 2}{start: 2, end: 1}表示相同的行。

I am trying to remove all duplicate lines from the array and cannot find an efficient or elegant way to do it. 我试图从数组中删除所有重复的行,并找不到有效或优雅的方法来做到这一点。 I have tried a nested loop but, I've been told that is bad practice (and I'm getting errors with my implementation, and it's just ugly). 我已经尝试了一个嵌套循环但是,我被告知这是不好的做法(我的实现有错误,而且它只是丑陋)。

for(var i = 0, numRoutes = routeArr.length; i < numRoutes; i++) {
    var primaryRoute = routeArr[i];

    for(var j = 0; j < numRoutes; j++) {
        var secondRoute = routeArr[j];

        if(primaryRoute.start === secondRoute.end && primaryRoute.end === secondRoute.start) {
            routeArr.splice(j, 1);
            continue;
        }
    }
}

Can anyone offer suggestions? 有人可以提供建议吗?

Create an object/map in javascript and keep the indexes of the unique objects, store "min(start,end):max(start,end)" as a key and index as a value. 在javascript中创建一个对象/映射并保留唯一对象的索引,将“min(start,end):max(start,end)”存储为键,将索引存储为值。 Here is an implementation of your question in javascript: 以下是javascript中您的问题的实现:

// your initial array
var routeArr = [
    {start: 1, end: 2},
    {start: 1, end: 3},
    {start: 1, end: 4},
    {start: 2, end: 1},
    {start: 3, end: 1},
    {start: 4, end: 1}
];

// map where we will store key => value where key is a joined start,end of your array's item and value is an item index 
var keyToRouteIndexMap = {};

for (var i in routeArr){
    // calculating min and max from start and end to understand {start:1, end:2} and {start:2, end:1} object as duplicates
    var min = Math.min(routeArr[i].start,routeArr[i].end);
    var max = Math.max(routeArr[i].start,routeArr[i].end);
    // unique key 
    var key = min+':'+max;
    if (!keyToRouteIndexMap.hasOwnProperty(key)){
        keyToRouteIndexMap[key] = i;
    }
}

for(var key in keyToRouteIndexMap){
    if(keyToRouteIndexMap.hasOwnProperty(key)){
        console.log(routeArr[keyToRouteIndexMap[key]]);
    }
}

Here is a general solution to the problem of removing duplicate values from javascript arrays: 以下是从javascript数组中删除重复值的问题的一般解决方案:

/**
 * Takes an input array and returns a new array without identical elements.
 *
 * @param {array} input
 * @callback id   identity function returning identical values for identical elements
 */
function uniquify(input, id) {
    result = [];
    map = {};
    for (var i = 0, length = input.length; i < length; ++i) {
        var element = input[i], identity = id(element);
        if (!map.hasOwnProperty(identity)) {
            result.push(element);
            map[identity] = true;
        }
    }
    return result;
}

Applied to your given routeArr : 应用于您的给定routeArr

var routeArr = [
    {start: 1, end: 2},
    {start: 1, end: 3},
    {start: 1, end: 4},
    {start: 2, end: 1},
    {start: 3, end: 1},
    {start: 4, end: 1}
];

routeArr = uniquify(routeArr, function(route) {
    return route.start < route.end ? '' + route.start + ':' + route.end : '' + route.end + ':' + route.start;
});

You can do like this. 你可以这样做。 I guess this is very fast since there are no searches at all. 我想这很快,因为根本没有搜索。 One Array.prototype.reduce() operation to construct both the hash table (lookup table) and the reduced object at the same time. 一个Array.prototype.reduce()操作同时构造哈希表(查找表)和缩减对象。 Then mapping the object keys to get the result. 然后映射对象键以获得结果。 Here it is; 这里是;

 var routeArr = [ {start: 1, end: 2}, {start: 1, end: 3}, {start: 1, end: 4}, {start: 2, end: 1}, {start: 3, end: 1}, {start: 4, end: 1} ], reduced = routeArr.reduce((p,c) => {!(p[c.start+"-"+c.end] || p[c.end+"-"+c.start]) && (p[c.start+"-"+c.end] = c); return p;},{}), result = Object.keys(reduced).map(e => reduced[e]); console.log(result); 

Well giving it a second thought i eliminated the redundant Object.keys() portion. 好吧,给它第二个想法我消除了冗余的Object.keys()部分。 Now this is nothing more than a single Array.prototype.reduce() pass all completed in just O(n). 现在这只是一个Array.prototype.reduce()传递所有在O(n)中完成的。 I suppose this might be as far as it gets concerning the performance. 我想这可能与性能有关。 Check it out. 看看这个。

 var routeArr = [ {start: 1, end: 2}, {start: 1, end: 3}, {start: 1, end: 4}, {start: 2, end: 1}, {start: 3, end: 1}, {start: 4, end: 1} ], reduced = routeArr.reduce((p,c) => {!(p[c.start+"-"+c.end] || p[c.end+"-"+c.start]) && (p[c.start+"-"+c.end] = true, p.result.push(c)); return p; },{"result":[]}); console.log(reduced.result); 

Well ok yes i have to agree it looks a little cryptic but it is very simple. 好吧是的我必须同意它看起来有点神秘,但它很简单。

  • We are using Array.prototype.reduce() method with an initial value here. 我们在这里使用带有初始值的Array.prototype.reduce()方法。 This is our initial value {"result":[]} . 这是我们的初始值{"result":[]} When reducing our routeArr array our initial element to start with is now an object with a single property named result and value of an empty array. 当减少我们的routeArr数组时,我们开始的初始元素现在是一个对象,它有一个名为result的属性和一个空数组的值。
  • reduce has been provided with an anonymous callback function which takes two arguments (p,c) p stands for previous and c stands for current. reduce已经提供了一个匿名回调函数,该函数有两个参数(p,c) p代表previous, c代表current。 So in the first run p is our initializing object, i mean this {"result":[]} and c is the item at index 0 of the array ( routeArr ) that we have called reduce upon. 所以在第一次运行中, p是我们的初始化对象,我的意思是这个{"result":[]}c是我们称之为reduce on的数组( routeArr )的索引0处的项。 So in the first round c is {start: 1, end: 2} . 所以在第一轮中c{start: 1, end: 2}
  • In the beginning of every round we check if our p object contains a property which represent the current elements values in both orders. 在每一轮的开始,我们检查我们的p对象是否包含表示两个订单中当前元素值的属性。 So the check comes like this !(p[c.start+"-"+c.end] || p[c.end+"-"+c.start]) which in human terms means "is it true that you don't have a string property like c.start-c.end or c.end-c.start".. So for example in the first round the check is like "is it true that you don't have a string property like "1-2" or "2-1". If it has (false) we do nothing but if it hasn't we perform the following actions; 因此支票就是这样的!(p[c.start+"-"+c.end] || p[c.end+"-"+c.start])在人的意义上意味着“你是不是真的” t有一个字符串属性,如c.start-c.end或c.end-c.start“..因此,例如在第一轮中,检查就像是”你是否真的没有像“这样的字符串属性” 1-2“或”2-1“。如果有(假),我们什么都不做,但如果没有,我们会采取以下行动;
  • && (p[c.start+"-"+c.end] = true, p.result.push(c)); return p; . OK the first && ties the two instructions in the parens to the condition of the previous instruction to evaluate to true. 好的,第一个&&将parens中的两个指令绑定到前一个指令的条件,以评估为true。 In a && b instruction JS engine will only evaluate b if a evaluates to true. a && b指令JS引擎将只评估b ,如果a计算结果为true。 So you got it. 所以你明白了。 Again in human terms this is what happens. 从人的角度来说,这就是发生的事情。 "is it true that you don't have a string property like "1-2" or "2-1" turns true and we create a property "1-2" with a value true. So in next rounds if we meet a 1-2 or 2-1 we will do nothing at all. Then we push this current object to the result property of the same object ( p.result ) to become a unique representative of all of it's duplicates or twins. Then we return p for a healthy continuation of the reduce cycles. “如果你没有像”1-2“或”2-1“这样的字符串属性变为true,我们创建一个属性为”1-2“并且值为true的情况是真的。所以在接下来的几轮中,如果我们遇到一个1-2或2-1我们什么都不做。然后我们将这个当前对象推送到同一个对象的结果属性( p.result ),成为所有重复或双胞胎的唯一代表。然后我们返回p为了健康延续减少周期。

I hope it is clear. 我希望很清楚。

Your nested loop methodology is 'ugly'- but that isn't your issue. 你的嵌套循环方法是“丑陋的” - 但这不是你的问题。

Your implementation errors are resulting from the fact that both of your for loops assume the array structure won't change as you're mutating it, which is causing you to skip over some items in the array. 你的实现错误是由于你的两个for循环都假设数组结构不会随着你的变化而改变,这导致你跳过数组中的一些项目。

'i' and 'j' are 'stupid' incrementers - That for loop isn't telling the code to go to the next item in the array with each iteration, it's telling it to go to (array[last_index_i_used+1] - So when you splice something the array you're looking at changes, and the next item in line gets passed over. 'i'和'j'是'愚蠢'的增量器 - for循环并没有告诉代码在每次迭代时转到数组中的下一个项目,它告诉它转到(array[last_index_i_used+1] - 所以当你剪切某个东西时,你正在寻找的数组会发生变化,并且下一个项目会被传递掉。

I see a lot of fancy array methods and ES6 suggestions, but I assume from your question that you are still a bit new to JS, and could use some time building fundamentals (no offense intended). 我看到了许多花哨的数组方法和ES6建议,但我从你的问题中假设你对JS仍然有点新,并且可以使用一些时间来构建基础(没有预期的冒犯)。

Try a recursive decrementing function: 尝试递归递减函数:

function uniquify(inputArray, ind){
    var checkStart = inputArray[ind].start, checkEnd =inputArray[ind].end
    for (var i=(ind-1);i > -1; --i){
        var thisStart = inputArray[i].start, thisEnd = inputArray[i].end
        if ((thisStart == checkStart || thisStart == checkEnd) && (thisEnd == checkStart || thisEnd == checkEnd)){

            inputArray.splice(i,1)
        }
    }

    --ind
    if (ind > -1){
        uniquify(inputArray,ind)
    }
}
uniquify(routeArr,routeArr.length -1);

I like that better than a nested for loop as you are never hitting the same value more often than you need to, which keeps performance consistent regardless of the size of your array. 我喜欢它比嵌套for循环更好,因为你永远不会比你需要更频繁地达到相同的值,这样无论数组的大小如何都能保持性能一致。

But you might want to ask yourself if whatever is defining 'routeArr' is doing whatever it's doing in a way that is intelligent - At best, it seems like it's wasting memory and CPU storing data in an inefficient way. 但是你可能想问自己,无论定义'routeArr'的是什么,它都以一种智能的方式做它正在做的事情 - 最好的情况是,它似乎浪费了内存和CPU以低效的方式存储数据。

I've wrote the function below to do it neatly 我已经编写了下面的函数,以便整齐地完成它

var routeArr = [{
  start: 1,
  end: 2
}, {
  start: 1,
  end: 3
}, {
  start: 1,
  end: 5
}, {
  start: 2,
  end: 1
}, {
  start: 3,
  end: 1
}, {
  start: 4,
  end: 1
}];

routeArr.IsDuplicate = function(obj) {
    var i = this.length;
    var count = 0 
    while (i--) {
        if ((this[i].start === obj.start && this[i].end === obj.end ) || (this[i].start === obj.end && this[i].end === obj.start) ) {
            count++;
        }
    }
    return count>1;
}

for(var i = routeArr.length-1; i--;){
    if (routeArr.IsDuplicate(routeArr[i])) routeArr.splice(i, 1);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM