Partial Lenses: Group array of objects by property, use prop value as key

ぐ巨炮叔叔 提交于 2019-12-18 09:28:37

问题


I have an array of objects like this:

[
  { name: "Group 1", value: "Foo" },
  { name: "Group 2", value: "Bar" },
  { name: "Group 1", value: "Baz" }
]

I'd like to use Partial Lenses library to transform these groups to keys of an object with corresponding group's items, like this:

{
  "Group 1": [
    { name: "Group 1", value: "Foo" },
    { name: "Group 1", value: "Baz" }
  ],
  "Group 2": [
    { name: "Group 2", value: "Bar" }
  ]
}

My current approach is like this, assuming I have the source data in a variable called data:

const grouped = L.collect([L.groupBy('name'), L.entries], data)
const setKey = [L.elems, 0]
const getName = [L.elems, 1, 0, 'name']
const correctPairs = L.disperse(setKey, L.collectTotal(getName, grouped), grouped)
L.get(L.inverse(L.keyed), correctPairs)

I don't like that I need to use the grouped and correctPairs variables to hold data, as I probably should be able to do the transformation directly in the composition. Could you help me to compose the same functionality in a more meaningful way?

Here's a Partial Lenses Playground with the above code.


回答1:


I assume the goal is to actually create an isomorphism through which one can view such an array as an object of arrays and also perform updates. Like a bidirectional version of e.g. Ramda's R.groupBy function.

Indeed, one approach would be to just use Ramda's R.groupBy to implement a new primitive isomorphism using L.iso. Something like this:

const objectBy = keyL => L.iso(
  R.cond([[R.is(Array), R.groupBy(L.get(keyL))]]),
  R.cond([[R.is(Object), L.collect([L.values, L.elems])]])
)

The conditionals are needed to allow for the possibility that the data is not of the expected type and to map the result to undefined in case it isn't.

Here is a playground with the above Ramda based objectBy implementation.

Using only the current version of Partial Lenses, one way to compose a similar objectBy combinator would be as follows:

const objectBy = keyL => [
  L.groupBy(keyL),
  L.array(L.unzipWith1(L.iso(x => [L.get(keyL, x), x], L.get(1)))),
  L.inverse(L.keyed)
]

Perhaps the interesting part in the above is the middle part that converts an array of arrays into an array of key-array pairs (or the other way around). L.unzipWith1 checks that all the keys within a group match, and if they don't, that group will be mapped to undefined and filtered out by L.array. If desired, it is possible to get stricter behaviour by using L.arrays.

Here is a playground with the above composed objectBy implementation.




回答2:


You don't need any library, use a generic function that returns a reducer, that way you can use to group any collection with any key. In the example below I used this to group by name, but also by value.

const groupBy = key => (result,current) => {
  let item = Object.assign({},current);
  // optional
  // delete item[key];
  if (typeof result[current[key]] == 'undefined'){
    result[current[key]] = [item];
  }else{
    result[current[key]].push(item);
  }
  return result;
};

const data = [{ name: "Group 1", value: "Foo" },{ name: "Group 2", value: "Bar" },{ name: "Group 1", value: "Baz" }];

const grouped = data.reduce(groupBy('name'),{});
console.log(grouped);
const groupedByValue = data.reduce(groupBy('value'),{});
console.log(groupedByValue);



回答3:


You can use Array.reduce

let arr = [{ name: "Group 1", value: "Foo" },{ name: "Group 2", value: "Bar" },{ name: "Group 1", value: "Baz" }];

let obj = arr.reduce((a,c) => Object.assign(a, {[c.name]: (a[c.name] || []).concat(c)}), {});
console.log(obj);


来源:https://stackoverflow.com/questions/53320017/partial-lenses-group-array-of-objects-by-property-use-prop-value-as-key

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!