从 GeoFire 对象数组中删除重复项 [重复]
Posted
技术标签:
【中文标题】从 GeoFire 对象数组中删除重复项 [重复]【英文标题】:Remove Duplicates from an Array of GeoFire Objects [duplicate] 【发布时间】:2019-02-28 00:47:31 【问题描述】:我正在使用 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"
Location基本上就是一个用来计算距离的经纬度数组,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(value, index, self)
return self.indexOf(value) === index;
var newlist = list.filter(onlyUnique)
不幸的是它没有工作......
这些是针对从数组中删除重复项的类似问题给出的一些答案,但没有一个有效。我不明白为什么它们不适用于我的数组类型,如果有人有想法或知道为什么会非常有帮助。
【问题讨论】:
您的方法不起作用,因为所有对象,即使它共享所有相同的键/值,都是不同的实例(除非这不是单例) 你能分享示例 JSON。这对我们会更好 不使用增量 id (0, 1, 2, 3,...) 作为道具名称但使用与对象相关的 id 是否也是一种解决方案?这将确保您没有重复记录。 你不能这样比较对象: ===
return false
这不是重复的,你是对的@RobG 我改成了 Geofire 对象
【参考方案1】:
您可以使用集合来存储和检查重复值。
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;
)
请记住,使用这种方法,您可能会删除距离相同但坐标不同的结果。如果这会给您带来问题,那么您还需要检查 lat/lng 对。
【讨论】:
位置是一个独特的元素,所以我们可以改变距离使用位置吗?【参考方案2】:这里的问题是比较对象。除非两个对象都引用同一个对象,否则两个对象永远不会相等。
示例:
=== // false
// Two objects are equal only if they are referencing to same object
var a = ;
a === a; // true
从您的问题中可以清楚地看出您正面临第一种情况。在您测试的解决方案中,Solution 1 和 Solution 3 都因为这个原因而失败,正如 indexOf
与 ===
的比较一样。
但是 Solution 2 应该对您的示例有效,因为它进行了深入比较,如此处所述。 https://lodash.com/docs#isEqual.
PS:这可能是我在解决方案2cleaned.,push(itm);
中观察到的一个简单的错字,有一个额外的逗号。希望不是这样我继续前进
所以我想问题出在您的位置数组内部,如果您可以提供位置数组的内容,我们应该能够提供更好的解决方案。或者正如其他人建议的那样,您可以基于对象的单个键进行过滤,例如 id 或 distance,而不是比较整个对象
【讨论】:
是的,尽管使用if (!_.isEqual(itm, itm2)) cleaned.push(itm);
可以更简单地编写解决方案2。 ;-)【参考方案3】:
您可以使用以下方法:
想法:
您可以创建自己的数据结构并使用 hashMap 来保存值。 由于您有位置数据,您可以使用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)
Typescript 版本更具可读性:
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] )
【讨论】:
这看起来很不错,你能把它翻译成打字稿吗??因为当你说 this.add 时,你在哪里声明 add? @ProgrammerMan 我添加了一个打字稿代码示例。它虽然无法运行。希望对你有帮助!【参考方案4】:uniqWith
https://lodash.com/docs/#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 ') );
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
【讨论】:
这行不通,因为它没有考虑每个对象的唯一 ID。 另外,你不能保证对象属性的顺序,所以不能保证 JSON.stringify 会是一个有用的比较。【参考方案5】:在添加匹配项之前,您可以随时检查以确保没有重复。
编辑:您无法比较对象,除非它们具有相同的参考对象。因此,您可以通过唯一 ID 比较对象
使用 rxjs 过滤器() 这将返回一个数组
// 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
【讨论】:
这种方法很好,但我收到错误 ts] 'void' 类型的 'this' 上下文不可分配给 'Observable' 类型的方法的 'this' 我在问题中显示的列表实际上可以从 console.log(this.hits.value) 或 console.log(currentHits) 中记录 @ProgrammerMan 这可能是从 rxjs 导入的错误。如果您使用的是 rxjs 6,请从 'rxjs/operators' 导入 distinctUntilChanged 感谢您检查我已经按照您所说的更改了导入,现在得到这个类型为 '(value: any[]) => any[]' 的参数不能分配给类型为 '( x: 任意 [], y: 任意 []) => 布尔值。类型 'any[]' 不能分配给类型 'boolean'。 @ProgrammerMan 很抱歉,实际上你不需要在运算符中传递值,我会更新答案【参考方案6】:也许您会想要使用像 lodash 这样的库,它具有关于所有类型集合的广泛函数集。
let newArr = _.uniqWith(myArr, _.isEqual);
uniqWith 在 isEqual 的帮助下可以得到你想要的。
这是该解决方案的fiddle
【讨论】:
【参考方案7】:我认为您的比较可能无法正常工作。你可以试试这个:
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;
, []);;
【讨论】:
以上是关于从 GeoFire 对象数组中删除重复项 [重复]的主要内容,如果未能解决你的问题,请参考以下文章
javascript 从javascript中的对象数组中删除重复项