【问题标题】:Remove Duplicates from an Array of GeoFire Objects [duplicate]从 GeoFire 对象数组中删除重复项 [重复]
【发布时间】: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 对象

标签: javascript angular firebase 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;
    }, []);;
    

    【讨论】:

      猜你喜欢
      • 2017-04-10
      • 1970-01-01
      • 1970-01-01
      • 2016-03-12
      • 2022-12-05
      相关资源
      最近更新 更多