我正在Angular 6上使用Geofire和Firebase来存储位置,不幸的是,它存储了大量重复项,这是一个示例(控制台记录我的变量 currentHits ):
0: {location: Array(2), distance: "48.84", url: "assets/imgs/fix.png"}
1: {location: Array(2), distance: "48.84", url: "assets/imgs/fix.png"}
2: {location: Array(2), distance: "48.84", url: "assets/imgs/fix.png"}
3: {location: Array(2), distance: "48.85", url: "assets/imgs/free.png"}
4: {location: Array(2), distance: "48.85", url: "assets/imgs/free.png"}
5: {location: Array(2), distance: "48.85", url: "assets/imgs/free.png"}
6: {location: Array(2), distance: "48.87", url: "assets/imgs/low.png"}
7: {location: Array(2), distance: "48.87", url: "assets/imgs/low.png"}
8: {location: Array(2), distance: "48.87", url: "assets/imgs/low.png"}
位置基本上是用于计算距离的经度和纬度数组,在id 0、1和2中,其坐标相同,而3,4和5也相同,...
这就是我想要得到的:
0: {location: Array(2), distance: "48.84", url: "assets/imgs/fix.png"}
1: {location: Array(2), distance: "48.85", url: "assets/imgs/free.png"}
2: {location: Array(2), distance: "48.87", url: "assets/imgs/low.png"}
(可选)这是存储这些位置的方式:
...
hits = new BehaviorSubject([])
...
queryHits(...){
....
let hit = {
location: location,
distance: distance.toFixed(2),
url:img
}
let currentHits = this.hits.value
currentHits.push(hit)
this.hits.next(currentHits)
....
}
的确,可能已经有人问过这个问题了,我一直在研究所有类似的问题并找到了这些功能:
1。 RemoveDuplicates()
function removeDuplicates(arr){
let unique_array = []
for(let i = 0;i < arr.length; i++){
if(unique_array.indexOf(arr[i]) == -1){
unique_array.push(arr[i])
}
}
return unique_array
}
var newlist = removeDuplicates(list)
这没用,我得到了重复的相同列表。
2。 arrUnique:
function arrUnique(arr) {
var cleaned = [];
arr.forEach(function(itm) {
var unique = true;
cleaned.forEach(function(itm2) {
if (_.isEqual(itm, itm2)) unique = false;
});
if (unique) cleaned.push(itm);
});
return cleaned;
}
var newlist= arrUnique(list);
也没有用。.
3。 onlyUnique
onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
var newlist = list.filter(onlyUnique)
不幸的是,它没有用...
这些是针对类似问题的一些答案,这些问题可以从数组中删除重复项,但都不起作用。我不明白为什么它们不适用于我的数组类型,如果有人有想法或知道为什么会很有帮助。
答案 0 :(得分:3)
您可以使用一组存储和检查重复值。
const removeDuplicates = arr => {
let matches = new Set();
return arr.filter(elem => {
const {distance} = elem;
if(matches.has(distance)){
return false;
} else {
matches.add(distance);
return true;
}
})
}
请记住,使用这种方法,您可以删除距离相同但坐标不同的结果。如果那对您造成问题,那么您还需要检查经纬度对。
答案 1 :(得分:3)
这里的问题是比较对象。除非两个对象都引用相同的对象,否则它们永远不会相等。
示例:
{} === {} // false
// Two objects are equal only if they are referencing to same object
var a = {};
a === a; // true
从您的问题中可以明显看出您正在面对第一种情况。在您测试的解决方案1 和解决方案3 的解决方案中,由于indexOf
做===
比较而导致的失败。
但是解决方案2 应该对您的示例有效,因为它可以进行深入的比较,如此处所述。 https://lodash.com/docs#isEqual。
PS:我在解决方案2 cleaned.,push(itm);
中观察到这可能是一个简单的错字,还有一个逗号。希望不是我要前进的情况
所以我想问题出在位置数组中,如果您可以提供位置数组的内容,我们应该能够提供更好的解决方案。或者像其他人建议的那样,您可以根据对象的单个键进行过滤,例如 id 或距离,而不是比较整个对象
答案 2 :(得分:1)
您始终可以在添加匹配之前进行检查,以确保没有重复。
编辑:您不能比较对象,除非它们具有相同的参考对象。因此,您可以通过唯一的ID比较对象
使用rxjs filter() 这将返回一个数组
// store history of objs for comparison
addedObjs = [];
this.hits.pipe(filter(obj => {
// check if id is an index of the previous objs
if (addObjs.indexOf(obj.id) === -1) {
this.addedObjs.push(obj.id)
return obj
});
这是使用您的某些代码的有效stackblitz
答案 3 :(得分:1)
p
https://lodash.com/docs/#uniqWith可用于指定比较方法:
uniqWith
var arr = [ { location: [1, 2], distance: "48.84", url: "assets/imgs/fix.png" },
{ location: [1, 2], distance: "48.84", url: "assets/imgs/fix.png" },
{ location: [1, 2], distance: "48.84", url: "assets/imgs/fix.png" },
{ location: [3, 4], distance: "48.85", url: "assets/imgs/free.png"},
{ location: [3, 4], distance: "48.85", url: "assets/imgs/free.png"},
{ location: [3, 4], distance: "48.85", url: "assets/imgs/free.png"},
{ location: [5, 6], distance: "48.87", url: "assets/imgs/low.png" },
{ location: [5, 6], distance: "48.87", url: "assets/imgs/low.png" },
{ location: [5, 6], distance: "48.87", url: "assets/imgs/low.png" } ]
var result = _.uniqWith(arr, (a, b) => _.isEqual(a.location, b.location));
console.log( JSON.stringify({...result}).replace(/},/g, '},\n ') );
答案 4 :(得分:1)
您可以使用以下方法:
longitude|latitude
作为键名,因为它是唯一的。add
,这些函数将检查值是否存在,并覆盖其他函数。value
,该属性将返回位置列表。 注意: 也可以使用Set
来实现上述行为。如果您不能使用ES6功能,那么这是一种可扩展且容易的方法。
function MyList() {
var locations = {};
this.add = function(value) {
var key = value.location.join('|');
locations[key] = value;
}
Object.defineProperty(this, 'value', {
get: function() {
return Object.keys(locations).map(function(key) {return locations[key] })
}
})
}
var locations = new MyList();
locations.add({location: [123.12, 456.23], name: 'test 1' });
locations.add({location: [123.16, 451.23], name: 'test 2' });
locations.add({location: [123.12, 456.23], name: 'test 1' });
locations.add({location: [100.12, 456.23], name: 'test 3' });
locations.add({location: [123.12, 456.23], name: 'test 1' });
locations.add({location: [123.12, 400.23], name: 'test 4' });
console.log(locations.value)
打字稿版本以提高可读性:
interface ILocation {
location: Array<number>
[key: string]: any;
}
interface IList {
[key: string]: ILocation
}
class MyList {
private locations: IList = {};
public add(value: ILocation) {
const key: string = value.location.join('|');
this.locations[key] = value;
}
public get value(): Array<ILocation> {
return Object.keys(locations).map(function(key) {return locations[key] })
}
}
答案 5 :(得分:0)
也许您想使用诸如lodash之类的库,该库具有涉及所有类型集合的广泛功能。
let newArr = _.uniqWith(myArr, _.isEqual);
uniqWith在isEqual的帮助下可以得到想要的东西。
这里是该解决方案的fiddle
答案 6 :(得分:0)
我认为您的比较可能无法正常工作。您可以尝试以下方法:
var uniqueHits = currentHits.reduce((acc, curr) => {
if (acc.length === 0) {
acc.push(curr);
} else if (!acc.some((item) =>
item.location[0] === curr.location[0]
&& item.location[1] === curr.location[1]
&& item.distance === curr.distance
&& item.url === curr.url)) {
acc.push(curr);
}
return accumulator;
}, []);;