从 GeoFire 对象数组中删除重复项

Remove Duplicates from an Array of GeoFire Objects

我在 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)

不幸的是它没有用...

这些是针对从数组中删除重复项的类似问题给出的一些答案,其中 none 有效。我不明白为什么它们不适用于我的阵列类型,如果有人有想法或知道为什么会很有帮助。

您可以使用集合来存储和检查重复值。

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 对。

您始终可以在添加匹配项之前进行检查,以确保没有重复项。

edit: 你不能比较对象,除非它们有相同的参考对象。因此,您可以通过唯一 ID

比较对象

使用 rxjs 过滤器() 这将 return 一个数组

// 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使用你的一些代码

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>

这里的问题是比较对象。除非两个对象都引用同一个对象,否则两个对象永远不相等。

示例:

{} === {} // 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); 中观察到的一个简单的错字,那里是一个额外的逗号。希望情况不是这样我继续前进

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

也许您会想要使用像 lodash 这样的库,它具有关于所有类型集合的广泛功能集。

let newArr = _.uniqWith(myArr, _.isEqual);

uniqWith借助isEqual可以得到你想要的。

这是fiddle那个解决方案

您可以使用以下方法:

想法:

  • 您可以创建自己的数据结构并使用 hashMap 来保存值。
  • 因为你有位置数据,你可以使用 longitude|latitude 作为你的键名,因为它是唯一的。
  • 然后暴露一些函数说,add 将检查值是否存在,覆盖其他添加。
  • 同时创建一个 属性,比如 value,它将 return 位置列表。

注意: 以上行为也可以使用 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] })
  }
}

我认为您的比较可能无法正常工作。你可以试试这个:

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;
}, []);;