[英]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的屬性和一個空數組的值。 (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.