【问题标题】:ES6 Map object to group valuesES6 将对象映射到组值
【发布时间】:2021-03-17 07:46:22
【问题描述】:

我想转换这个数组:

[{
  department: 'HR',
  person: 'Tom'
},{
  department: 'Finance',
  person: 'Peter'
},{
  department: 'HR',
  person: 'Jane'
}];

进入这个,按department分组人并更改密钥

[{
  role: 'HR',
  people: ['Tom','Jane']
 },{
  role: 'Finance',
  people: ['Peter']
}]

我使用这种看起来很流行并且效果很好的技术,我不确定它是否有名字。

const data = [{department: 'HR', person: 'Tom'},{department: 'Finance',person: 'Peter'},{department: 'HR',person: 'Jane'}];

function groupPeopleByDepartmentWithObj(data) {
  const obj = {};
  for (const { department, person} of data) {
    if (!obj[department]) {
      obj[department] = { role: department, people: []};
    }
    obj[department].people.push(person);
  }
  return Object.values(obj);
}

console.log(groupPeopleByDepartmentWithObj(data));
.as-console-wrapper { max-height: 100% !important; top: 0; }

使用 ES6 Map 对象我可以这样做

const data = [{department: 'HR', person: 'Tom'},{department: 'Finance',person: 'Peter'},{department: 'HR',person: 'Jane'}];

function groupPeopleByDepartmentWithMap(data)  {

  const mapper = new Map()
  for (const { department, person} of data) {
    if (!mapper.has(department)) {
      mapper.set(department, { role: department, people: []}) 
    }
    mapper.get(department).people.push(person)
  }
  return Array.from(mapper.values())
}

console.log(groupPeopleByDepartmentWithMap(data))
.as-console-wrapper { max-height: 100% !important; top: 0; }

这种方法在任何方面都更好吗,还有其他方法可以使用Map 来获得相同的结果吗?

【问题讨论】:

  • 当你说Is this approach better是什么意思?您是否正在尝试(可能过早地)优化代码的性能?还是您正在寻找错误?
  • 也许这个问题最好放在codereview.stackexchange.com
  • 我的意思是,使用地图来完成这项任务有什么意义吗?
  • 您在征求意见,而基于意见的问题不适合 SO
  • 如果您有department 的数值,则使用地图将保留原始顺序,而使用对象则不会(例如:如果将department 更改为具有,您将获得两个不同的顺序2 而不是 'HR'1 而不是 'Finance')。如果department 持有对象类型,您还将在两者之间得到不同的结果。在速度方面,我听说使用地图会更快,但我自己从未测试过,因为这样的事情的速度真的不应该受到关注

标签: javascript arrays ecmascript-6 javascript-objects


【解决方案1】:

对象属性和键值存储中的之间存在根本区别。

属性是您在编码时定义的东西,它具有固定的名称和明确的含义。属性类似于变量或函数。

键是只有在运行时才知道的东西,它是动态的,除了与某个值相关联之外没有任何内在意义。键就像一个数组索引。

过去,javascript Objects 被滥用于模拟键值存储,使用属性名称作为键。这有几个严重的缺点

  • 属性名和键只能是字符串
  • 属性通常没有顺序
  • 关键迭代很笨拙(hasOwnProperty 等)
  • 由于属性和键之间没有区别,动态键可能会意外覆盖已定义的属性,反之亦然,现有属性可能会被误认为是键

Map 专门针对这些缺点而发明:

  • Map 键可以是任何东西
  • Map 键始终按插入顺序排列
  • 关键迭代定义明确
  • 键和属性位于不同的命名空间中,不会相互覆盖

因此,如果您需要键值存储,Map 始终是更好的选择。为此使用泛型对象是错误的。

至于使用地图进行分组的“更好”方式,这是相当主观的。我更喜欢更通用的版本

/// data: an Iterable
/// keyFn: a function which will be applied to each data item to obtain its group key
/// returns: a Map(key => [items])

// function groupBy<T, K>(data: Iterable<T>, keyFn: (x: T) => K): Map<K, T[]> 
//
function groupBy(data, keyFn) {
    let m = new Map();

    for (let x of data) {
        let k = keyFn(x);
        if (!m.has(k))
            m.set(k, []);
        m.get(k).push(x);
    }

    return m;
}

可以像这样用于手头的任务:

let result = [];

for (let [role, items] of groupBy(data, x => x.department))
    result.push({role, people: items.map(x => x.person)})

【讨论】:

  • 那么你是说第二个 sn-p 更适合这个任务?
  • @Álvaro: 是的,我就是这么说的 ;)
  • 谢谢,关于另一个问题,Map 有没有更好的方法?因为我只是在复制我对对象所做的事情。
  • 实际上该代码将一个数组推入people数组
  • @Álvaro: 是的,已修复
猜你喜欢
  • 2020-03-19
  • 1970-01-01
  • 1970-01-01
  • 2017-10-30
  • 1970-01-01
  • 1970-01-01
  • 2020-07-22
  • 2021-04-20
  • 2022-11-24
相关资源
最近更新 更多