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

时间:2018-09-24 13:25:15

标签: javascript angular firebase geofire

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

不幸的是,它没有用...

这些是针对类似问题的一些答案,这些问题可以从数组中删除重复项,但都不起作用。我不明白为什么它们不适用于我的数组类型,如果有人有想法或知道为什么会很有帮助。

7 个答案:

答案 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)

您可以使用以下方法:

想法:

  • 您可以创建自己的数据结构,并具有一个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)

打字稿版本以提高可读性:

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