【问题标题】:How to filter a JavaScript Map?如何过滤 JavaScript 地图?
【发布时间】:2018-02-09 13:42:20
【问题描述】:

给定一个 ES6 Map 和谓词函数,我如何安全地删除地图的所有不匹配元素?


我找不到官方的 API 函数,但我可以想到两个实现。第一个不会尝试就地删除,而是创建一个副本:

// version 1:
function filter(map, pred) {
  const result = new Map();
  for (let [k, v] of map) {
    if (pred(k,v)) {
      result.set(k, v);
    }
  }
  return result;
}

const map = new Map().set(1,"one").set(2,"two").set(3,"three");
const even = filter(map, (k,v) => k % 2 === 0);
console.log([...even]); // Output: "[ [ 2, 'two' ] ]"

另一个就地删除。在我的测试中,它可以工作,但我没有发现修改地图不会破坏迭代器(for-of 循​​环)的保证:

// version 2:
function deleteIfNot(map, pred) {
  for (let [k, v] of map) {
    if (!pred(k,v)) {
      map.delete(k);
    }
  }
  return map;
}

const map = new Map().set(1,"one").set(2,"two").set(3,"three");
deleteIfNot(map, (k,v) => k % 2 === 0);
console.log([...map]); // Output: "[ [ 2, 'two' ] ]"

问题:

  • 第二个版本(就地删除)在所有平台上都正确吗?
  • 有没有更好的方法来实现就地过滤器?也许我错过了一些官方 API?

【问题讨论】:

  • 使用函数filter 处理数组意味着您将要修改克隆并返回它。就地修改不是功能(没有副作用)。如果真的要原地修改,只需重新赋值给filter函数的结果即可。

标签: javascript ecmascript-6


【解决方案1】:

如果我们想使用 .filter() 迭代器,我们可以应用一个简单的技巧,因为 ES6 Maps 没有 .filter 运算符。Dr. Axel Rauschmayer 的方法是:

  • 将映射转换为 [key,value] 对数组。
  • 映射或过滤数组。
  • 将结果转换回地图。

例子:

const map0 = new Map([
  ['a', 1],
  ['b', 2],
  ['c', 3]
]);

const map1 = new Map(
  [...map0]
  .filter(([k, v]) => v < 3 )
);

console.info([...map1]); 
//[0: ["a", 1], 1: ["b", 2]]

【讨论】:

  • 我很好奇与接受的答案相比,它的性能如何。中间数组需要分配吗?
  • 我不确定。这是测试研究的问题。
  • @Lukeyb [...map0].filter(...) 是这里的 2 个中间数组。当然,数组会产生一定的开销,对于额外的地图甚至更多,这比修改现有地图要慢很多倍。但仍然可以足够快,只要每秒不处理数千个元素,性能就不是问题。无论如何,如果案例不需要旧地图是不可变的,那么创建新地图就是浪费资源。
  • @Estus_Flask,在我看来,这里没有“中间”数组。此处给出 map0 以显示输入数据结构。所以任务是如果你有 map0(具有这种结构),如何过滤它并将结果作为另一个地图?当然,如果我们有一个数组作为输入,那么我们就不需要任何映射。
  • [...map0] 的另一种写法是Array.from(map0)
【解决方案2】:

在循环中删除条目时,ES6 可迭代对象没有问题。

没有特殊的 API 可以有效地过滤 ES6 映射条目而无需对其进行迭代。

如果地图不必是不可变的并且应该就地修改,则在过滤时创建新地图会产生开销。

还有 Map forEach,但它假定也将使用该值。

由于地图仅按其键过滤,因此条目对象没有用处。它可以通过迭代映射键来改进:

for (let k of map.keys()) {
  if (!(k % 2))
    map.delete(k);
}

【讨论】:

  • 在我的真实示例中,pred 使用了该值。但你的回答涵盖了我想知道的一切。我不确定迭代并同时修改地图。在其他一些语言(例如,C++、Java)中,这会中断,或者只有在您直接对迭代器进行操作时才有效。
【解决方案3】:
// Given a map with keys a, b

const m = new Map()
m.set('a', 1)
m.set('b', 2)

// Return a new map that do not match key a

new Map([...m].filter(([k, v])=>k!=='a'))

// Return a new map only matching value 2

new Map([...m].filter(([k, v])=>v===2))

// Filter in place, removing all values not 2

[...m].map((i)=>i[1]!==2 && m.delete(i[0]))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-27
    • 1970-01-01
    • 2020-10-10
    • 2019-10-01
    • 1970-01-01
    • 2021-11-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多