微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

ES6 映射对象以对值进行分组

如何解决ES6 映射对象以对值进行分组

我想转换这个数组:

[{
  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: '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 来获得相同的结果吗?

解决方法

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

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

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

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

  • 属性名称和键只能是字符串
  • 属性通常不排序
  • 关键迭代很笨拙(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)})

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。