部分镜头:按 属性 对对象数组进行分组,使用道具值作为键

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

我有一个这样的对象数组:

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

我想使用 Partial Lenses 库将这些组转换为具有相应组项的对象的键,如下所示:

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

我目前的方法是这样的,假设我有一个名为 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)

我不喜欢我需要使用 groupedcorrectPairs 变量来保存数据,因为我可能应该能够直接在合成中进行转换。你能帮助我以更有意义的方式组合相同的功能吗?

这是一个包含上述代码的 Partial Lenses Playground

您可以使用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);

您不需要任何库,使用 returns 缩减器的通用函数,这样您就可以使用任何键对任何集合进行分组。在下面的示例中,我使用它按名称分组,但也按值分组。

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

我假设目标是实际创建一个同构,通过它可以 将这样的数组视为数组的对象并执行更新。像一个 例如的双向版本拉姆达的 R.groupBy 函数。

确实,一种方法是只使用 Ramda 的 R.groupBy 实现一个新的原语 使用 L.iso 的同构。 像这样:

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])]])
)

需要条件来考虑数据不属于的可能性 预期的类型并将结果映射到 undefined 以防它不是。

这是一个基于上述 Ramda 的游乐场 objectBy 实施。

仅使用当前版本的Partial Lenses,一种合成类似的方法 objectBy 组合子如下:

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)
]

也许上面有趣的部分是中间的部分,将一个 数组的数组转换为键数组对的数组(或相反)。 L.unzipWith1 检查组中的所有键是否匹配,如果不匹配,则该组 将被映射到 undefined 并被过滤掉 L.array。如果需要, 可以通过使用获得更严格的行为 L.arrays.

这里是一个由上述组成的游乐场 objectBy 实施。