簡體   English   中英

從Javascript數組中刪除等效但唯一的對象

[英]Removing equivalent but unique objects from a Javascript 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}
];

這些對象表示行的起點和終點,因此{start: 1, end: 2}{start: 2, end: 1}表示相同的行。

我試圖從數組中刪除所有重復的行,並找不到有效或優雅的方法來做到這一點。 我已經嘗試了一個嵌套循環但是,我被告知這是不好的做法(我的實現有錯誤,而且它只是丑陋)。

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;
        }
    }
}

有人可以提供建議嗎?

在javascript中創建一個對象/映射並保留唯一對象的索引,將“min(start,end):max(start,end)”存儲為鍵,將索引存儲為值。 以下是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]]);
    }
}

以下是從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;
}

應用於您的給定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;
});

你可以這樣做。 我想這很快,因為根本沒有搜索。 一個Array.prototype.reduce()操作同時構造哈希表(查找表)和縮減對象。 然后映射對象鍵以獲得結果。 這里是;

 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); 

好吧,給它第二個想法我消除了冗余的Object.keys()部分。 現在這只是一個Array.prototype.reduce()傳遞所有在O(n)中完成的。 我想這可能與性能有關。 看看這個。

 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); 

好吧是的我必須同意它看起來有點神秘,但它很簡單。

  • 我們在這里使用帶有初始值的Array.prototype.reduce()方法。 這是我們的初始值{"result":[]} 當減少我們的routeArr數組時,我們開始的初始元素現在是一個對象,它有一個名為result的屬性和一個空數組的值。
  • reduce已經提供了一個匿名回調函數,該函數有兩個參數(p,c) p代表previous, c代表current。 所以在第一次運行中, p是我們的初始化對象,我的意思是這個{"result":[]}c是我們稱之為reduce on的數組( routeArr )的索引0處的項。 所以在第一輪中c{start: 1, end: 2}
  • 在每一輪的開始,我們檢查我們的p對象是否包含表示兩個訂單中當前元素值的屬性。 因此支票就是這樣的!(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; 好的,第一個&&將parens中的兩個指令綁定到前一個指令的條件,以評估為true。 a && b指令JS引擎將只評估b ,如果a計算結果為true。 所以你明白了。 從人的角度來說,這就是發生的事情。 “如果你沒有像”1-2“或”2-1“這樣的字符串屬性變為true,我們創建一個屬性為”1-2“並且值為true的情況是真的。所以在接下來的幾輪中,如果我們遇到一個1-2或2-1我們什么都不做。然后我們將這個當前對象推送到同一個對象的結果屬性( p.result ),成為所有重復或雙胞胎的唯一代表。然后我們返回p為了健康延續減少周期。

我希望很清楚。

你的嵌套循環方法是“丑陋的” - 但這不是你的問題。

你的實現錯誤是由於你的兩個for循環都假設數組結構不會隨着你的變化而改變,這導致你跳過數組中的一些項目。

'i'和'j'是'愚蠢'的增量器 - for循環並沒有告訴代碼在每次迭代時轉到數組中的下一個項目,它告訴它轉到(array[last_index_i_used+1] - 所以當你剪切某個東西時,你正在尋找的數組會發生變化,並且下一個項目會被傳遞掉。

我看到了許多花哨的數組方法和ES6建議,但我從你的問題中假設你對JS仍然有點新,並且可以使用一些時間來構建基礎(沒有預期的冒犯)。

嘗試遞歸遞減函數:

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);

我喜歡它比嵌套for循環更好,因為你永遠不會比你需要更頻繁地達到相同的值,這樣無論數組的大小如何都能保持性能一致。

但是你可能想問自己,無論定義'routeArr'的是什么,它都以一種智能的方式做它正在做的事情 - 最好的情況是,它似乎浪費了內存和CPU以低效的方式存儲數據。

我已經編寫了下面的函數,以便整齊地完成它

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