简体   繁体   中英

ES6 Map object to group values

I want to convert this array:

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

Into this one, grouping people by department and changing the keys

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

I use this technique that have seem around and works really well, I am not really sure if it's got a name.

 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; }

Using the ES6 Map object I can do the same with this

 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; }

Is this approach better in any way, is there another way to use Map to get the same result?

There's a fundamental difference between object properties and keys in a key-value store.

A property is something you define at the coding time, it has a fixed name and a well-defined meaning. A property is similar to a variable or a function.

A key is something that is only known at the run time, it is dynamic and has no inherent meaning except being associated with some value. A key is like an array index.

In the past, javascript Object s were misused to emulate key-value stores, using property names as keys. This had several serious drawbacks

  • property names, and hence keys, can only be strings
  • properties are generally not ordered
  • key iteration is clumsy ( hasOwnProperty etc)
  • since there's no difference between properties and keys, a dynamic key can accidentally overwrite a defined property, and vice versa, an existing property can be mistaken for a key

The Map was invented to address these drawbacks specifically:

  • Map keys can be anything
  • Map keys are always in insertion order
  • key iteration is well defined
  • keys and properties live in separate namespaces and don't overwrite each other

Therefore, if you need a key-value store, Map is always a better choice. Using generic Objects for this is a mistake.

As for the "better" way to use Maps for grouping, this is rather subjective. I'd prefer a more generic version

/// 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;
}

which can be used like this for the task at hand:

let result = [];

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM