使用lodash从数组中过滤冗余对象

时间:2016-08-19 15:45:38

标签: javascript lodash

数据

var ranges = [
        { start: 2, end: 5 },
        { start: 8, end: 12 },
        { start: 15, end: 20 },
        { start: 9, end: 11 },
        { start: 2, end: 6 }
    ];

每个对象代表一个范围。我需要删除包含在另一个中的范围。 也就是说,在两个冗余对象之间,我需要保持更长的范围

我编写了这段代码,但我想知道是否有更好的方法可以使用lodash实现这一目标。

var Range = {
    contains: function(r1, r2) {
        return r2.start >= r1.start && r2.end <= r1.end;
    }
};

var result = _.chain(ranges)
    .filter(function(r2) {
        return !_.some(ranges, function(r1) {
            return r1 != r2 && Range.contains(r1, r2);
        });
    })
    .value();

console.log(result);

输出

[
    { start: 8, end: 12 },
    { start: 15, end: 20 },
    { start: 2, end: 6 }
]

4 个答案:

答案 0 :(得分:3)

我终于找到了一种使用uniqWith实现此目的的简单方法。

var result = _.chain(ranges)
    .orderBy(['end'], ['desc'])
    .uniqWith(function(r1, r2) {
        return Range.contains(r2, r1);
    })
    .value();

答案 1 :(得分:2)

我非常喜欢您的解决方案,但如果您愿意,可以使用emulator将其写得更紧凑:

var ranges = [
  { start: 2, end: 5 },
  { start: 8, end: 12 },
  { start: 15, end: 20 },
  { start: 9, end: 11 },
  { start: 2, end: 6 }
];

var result = _.differenceWith(ranges, ranges, function(r1, r2) {
  return !_.isEqual(r1, r2) && r1.start >= r2.start && r1.end <= r2.end;
});

console.log(result);

&#13;
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
&#13;
function between(r1, r2) {
  return r1.start >= r2.start && r1.end <= r2.end;
}

var i = ranges.length;
while(i) {
  --i;
  var r1 = ranges[i];
  var j = i;
  while(j) {
    --j;
    var r2 = ranges[j];
    if(between(r1, r2)) {
      ranges.splice(i, 1);
      break;
    } else if (between(r2, r1)) {
      ranges.splice(j, 1);
      --i;
    }
  }
}
&#13;
&#13;
&#13;

当您没有那么多范围时,这些解决方案非常有用。如果你有一个大型数组,那么可能值得使用两个常规while循环:

var ranges = [
  { start: 2, end: 5 },
  { start: 8, end: 12 },
  { start: 15, end: 20 },
  { start: 9, end: 11 },
  { start: 2, end: 6 }
];

function between(r1, r2) {
  return r1.start >= r2.start && r1.end <= r2.end;
}

var i = ranges.length;
while(i) {
  --i;
  var r1 = ranges[i];
  var j = i;
  while(j) {
    --j;
    var r2 = ranges[j];
    if(between(r1, r2)) {
      ranges.splice(i, 1);
      break;
    } else if (between(r2, r1)) {
      ranges.splice(j, 1);
      --i;
    }
  }
}

console.log(ranges);

&#13;
&#13;
JavaScript
&#13;
&#13;
&#13;

答案 2 :(得分:2)

如果您想避免对大型数据集进行冗余比较,可以在比较范围之前对范围进行排序:

var sorted = _.orderBy(ranges, ['start', 'end'], ['desc', 'asc']);

var result = _.chain(sorted)
    .filter(function(r2, i) {
        //Only the items after r2 may contain r2 so we slice the array.
        return !_.some(_.slice(sorted,i+1), function(r1) {
            // You could just test if r2.end <= r1.end here
            // We already know that r2.start >= r1.start
            return Range.contains(r1, r2);
        });
    })
    .value();

r2之前的所有项目都在r2之后开始,但不能包含它,因此我们不会对它们进行比较。

我不认为lodash提供任何方法来开箱即用。

答案 3 :(得分:1)

普通的JavaScript(ES6)不是一个选项吗?

ranges = ranges
    .sort( (a,b) => a.start - b.start || b.end - a.end )
    .reduce( (acc,a,i,ranges) => a.end > acc[1] ? [acc[0].concat([a]), a.end] : acc, [[],0] )
    [0];

&#13;
&#13;
var ranges = [
    { start: 2, end: 5 },
    { start: 8, end: 12 },
    { start: 15, end: 20 },
    { start: 9, end: 11 },
    { start: 2, end: 6 }
];

ranges = ranges
    .sort( (a,b) => a.start - b.start || b.end - a.end )
    .reduce( (acc,a,i,ranges) => a.end > acc[1] ? [acc[0].concat([a]), a.end] : acc, [[],0] )
    [0];

console.log(ranges);
&#13;
&#13;
&#13;