从 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 1Solution 3 都因为这个原因而失败,正如 indexOf=== 的比较一样。

但是 Solution 2 应该对您的示例有效,因为它进行了深入比较,如此处所述。 https://lodash.com/docs#isEqual.

PS:这可能是我在解决方案2cleaned.,push(itm);中观察到的一个简单的错字,有一个额外的逗号。希望不是这样我继续前进

所以我想问题出在您的位置数组内部,如果您可以提供位置数组的内容,我们应该能够提供更好的解决方案。或者正如其他人建议的那样,您可以基于对象的单个键进行过滤,例如 iddistance,而不是比较整个对象

【讨论】:

是的,尽管使用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】:

uniqWithhttps://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 ') );
&lt;script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"&gt;&lt;/script&gt;

【讨论】:

这行不通,因为它没有考虑每个对象的唯一 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 对象数组中删除重复项 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

从对象数组中的嵌套数组中删除重复项

如何从对象数组中删除所有重复项?

ES6 - 从对象数组中删除重复项

javascript 从javascript中的对象数组中删除重复项

使用 JavaScript 从数组中删除所有重复项 [重复]

使用 Set 从数组中删除重复项,保持最高 id