group data using a vector of keys in JavaScript, add empty array where key is not in data

huangapple go评论59阅读模式
英文:

group data using a vector of keys in JavaScript, add empty array where key is not in data

问题

我想使用这个键的向量

const vec_of_vals = ["one", "two", "three", "four"]

来对对象数组进行分组和筛选

const data = [
  { grade: "one" },
  { grade: "one" },
  { grade: "one" },
  { grade: "two" },
  { grade: "four" },
  { grade: "four" },
  { grade: "four" },
  { grade: "four" },
  { grade: "five" },
  { grade: "five" },
  { grade: "five" },
  { grade: "six" }
]

我找到了可以帮助我做到这一点的帖子,但它们不包括初始 vec_of_vals 中的空 three 数组。

这是我尝试过的内容

function groupByKey(array, key) {
  return array.reduce((hash, obj) => {
    if (obj[key] === undefined) return hash;
    return Object.assign(hash, {
      [obj[key]]: (hash[obj[key]] || []).concat(obj),
    });
  }, {});
}

groupByKey(
  data.filter((x) => vec_of_vals.includes(x.grade)),
  "grade"
);

但这不会给我空的 three 数组,并且它不会保持原始数据的顺序!以下是我期望的输出:

期望的输出

let desired_output = {
  "one": [
    { grade: "one" },
    { grade: "one" }
  ],
  "two": [
    { grade: "two" }
  ],
  "three": [],
  "four": [
    { grade: "four" },
    { grade: "four" },
    { grade: "four" },
    { grade: "four" }
  ]
}

如何修改 groupByKey 函数,不仅按 grade 分组,还要插入任何缺失的成绩并删除原始键中不存在的任何成绩,同时保持原始 vec_of_vals 的顺序?

英文:

I'd like to use this vector of keys

const vec_of_vals = ["one", "two", "three", "four"]

to group and filter an array of objects

const data = [
  {grade: "one"},
  {grade: "one"},
  {grade: "one"},
  {grade: "two"},
  {grade: "four"},
  {grade: "four"},
  {grade: "four"},
  {grade: "four"},
  {grade: "five"},
  {grade: "five"},
  {grade: "five"},
  {grade: "six"}
]

And I've found posts that can help me to do this, but they don't include an empty "three" array which is in my initial vec_of_vals

Here's what I've tried

function groupByKey(array, key) {
  return array.reduce((hash, obj) => {
    if (obj[key] === undefined) return hash;
    return Object.assign(hash, {
      [obj[key]]: (hash[obj[key]] || []).concat(obj),
    });
  }, {});
}

groupByKey(
  data.filter((x) => vec_of_vals.includes(x.grade)),
  "grade"
);

But this wont give me the empty three array AND it doesn't keep the original order of my data!. As a visual here is my desired output:

Desired Output

let desired_output = {
  "one" : [
    {grade: "one"},
    {grade: "one"},
  ],
  "two" : [
    {grade: "two"},
  ],
  "three" : [],
  "four" : [
    {grade: "four"},
    {grade: "four"},
    {grade: "four"},
    {grade: "four"}
  ]
}

How do I change the groupByKey function to not only group by grade but insert any missing grades and remove any grades that are not in the original key as well as maintain the order of the original vec_of_vals?

答案1

得分: 1

你可以在你的成绩结果上使用 Array::reduce()

const grades = 'abcd'.split('');
const data = [
    { grade: "a" }, { grade: "a" }, { grade: "a" }, { grade: "b" }, { grade: "d" }, { grade: "d" }, { grade: "d" }, { grade: "d" }, { grade: "e" }, { grade: "e" }, { grade: "e" }, { grade: "f" }
];

const grouped = grades.reduce((grouped, key) => (grouped[key] = data.filter(({grade}) => grade === key)) && grouped, {});

console.log(JSON.stringify(grouped));

或者你可以遍历你的数据数组(这样做速度会更快):

const grades = 'abcd'.split('');
const data = [
    { grade: "a" }, { grade: "a" }, { grade: "a" }, { grade: "b" }, { grade: "d" }, { grade: "d" }, { grade: "d" }, { grade: "d" }, { grade: "e" }, { grade: "e" }, { grade: "e" }, { grade: "f" }
];

const grouped = grades.reduce((grouped, grade) => (grouped[grade] = []) && grouped, {});

data.forEach(item => grouped[item.grade]?.push(item));

console.log(JSON.stringify(grouped));

以及性能基准:

<script benchmark data-count="1">

    const grades = 'abcd'.split('');

    const data = JSON.parse(JSON.stringify(Array.from({ length: 300000 }).reduce(arr => arr.push(...[
        { grade: "a" }, { grade: "a" }, { grade: "a" }, { grade: "b" }, { grade: "d" }, { grade: "d" }, { grade: "d" }, { grade: "d" }, { grade: "e" }, { grade: "e" }, { grade: "e" }, { grade: "f" }
    ]) && arr, [])));

    // @benchmark reducing grades and filtering data

    grades.reduce((grouped, key) => (grouped[key] = data.filter(({ grade }) => grade === key)) && grouped, {});

    // @benchmark iterating data array

    const grouped = grades.reduce((grouped, grade) => (grouped[grade] = []) && grouped, {});

    data.forEach(item => grouped[item.grade]?.push(item));
    grouped;


</script>
<script src="https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js"></script>

group data using a vector of keys in JavaScript, add empty array where key is not in data

英文:

You could use Array::reduce() on your result grades:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const grades = &#39;abcd&#39;.split(&#39;&#39;);
const data = [
        { grade: &quot;a&quot; }, { grade: &quot;a&quot; }, { grade: &quot;a&quot; }, { grade: &quot;b&quot; }, { grade: &quot;d&quot; }, { grade: &quot;d&quot; }, { grade: &quot;d&quot; }, { grade: &quot;d&quot; }, { grade: &quot;e&quot; }, { grade: &quot;e&quot; }, { grade: &quot;e&quot; }, { grade: &quot;f&quot; }
    ];

const grouped = grades.reduce((grouped, key) =&gt; (grouped[key] = data.filter(({grade}) =&gt; grade === key)) &amp;&amp; grouped, {});

console.log(JSON.stringify(grouped));

<!-- end snippet -->

Or you could iterate your data array (which is twice faster):

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const grades = &#39;abcd&#39;.split(&#39;&#39;);
const data = [
        { grade: &quot;a&quot; }, { grade: &quot;a&quot; }, { grade: &quot;a&quot; }, { grade: &quot;b&quot; }, { grade: &quot;d&quot; }, { grade: &quot;d&quot; }, { grade: &quot;d&quot; }, { grade: &quot;d&quot; }, { grade: &quot;e&quot; }, { grade: &quot;e&quot; }, { grade: &quot;e&quot; }, { grade: &quot;f&quot; }
    ];

const grouped = grades.reduce((grouped, grade) =&gt; (grouped[grade] = []) &amp;&amp; grouped, {});

data.forEach(item =&gt; grouped[item.grade]?.push(item));

console.log(JSON.stringify(grouped));

<!-- end snippet -->

And the benchmark:
group data using a vector of keys in JavaScript, add empty array where key is not in data

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-html -->

&lt;script benchmark data-count=&quot;1&quot;&gt;

    const grades = &#39;abcd&#39;.split(&#39;&#39;);

    const data = JSON.parse(JSON.stringify(Array.from({ length: 300000 }).reduce(arr =&gt; arr.push(...[
        { grade: &quot;a&quot; }, { grade: &quot;a&quot; }, { grade: &quot;a&quot; }, { grade: &quot;b&quot; }, { grade: &quot;d&quot; }, { grade: &quot;d&quot; }, { grade: &quot;d&quot; }, { grade: &quot;d&quot; }, { grade: &quot;e&quot; }, { grade: &quot;e&quot; }, { grade: &quot;e&quot; }, { grade: &quot;f&quot; }
    ]) &amp;&amp; arr, [])));

    // @benchmark reducing grades and filtering data

    grades.reduce((grouped, key) =&gt; (grouped[key] = data.filter(({ grade }) =&gt; grade === key)) &amp;&amp; grouped, {});

    // @benchmark iterating data array

    const grouped = grades.reduce((grouped, grade) =&gt; (grouped[grade] = []) &amp;&amp; grouped, {});

    data.forEach(item =&gt; grouped[item.grade]?.push(item));
    grouped;


&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js&quot;&gt;&lt;/script&gt;

<!-- end snippet -->

答案2

得分: 0

以下是翻译好的部分:

One possible and pretty straightforward approach would be based on two single reduce tasks.

首先,将data数组减少为一个对象,该对象根据其grade值对数据项进行分组和收集...

const gradeCollection = data
  .reduce((collection, dataItem) => {

    const { grade } = dataItem;
    (collection[grade] ??= []).push(dataItem);

    return collection;
  }, {});

最终结果可以从提供的成绩优先级列表(OP称之为“键的向量”或vec_of_vals)和刚刚处理过的gradeCollection中计算出来。

为了使result对象的键值对(条目)与已提到的gradePrecedenceList(以前称为vec_of_vals)的成绩值对齐,可以对后者进行reduce,在每次迭代中创建一个条目,其中键等于当前的grade值,值从之前创建的gradeCollection中以相同的grade值查找。如果查找不成功,将分配一个空数组。

const result = gradePrecedenceList
  .reduce((collection, grade) => {

    collection[grade] = gradeCollection[grade] ?? [];
    return collection;
  }, {});

...可执行的示例代码证明了上述说法...

英文:

One possible and pretty straightforward approach would be based on two single reduce tasks.

First one reduces the data array into an object which groups and collects the data items each by its grade-value ...

const gradeCollection = data
  .reduce((collection, dataItem) =&gt; {

    const { grade } = dataItem;
    (collection[grade] ??= []).push(dataItem);

    return collection;
  }, {});

The final result already can be computed from both, the also provided grade-precedence list (what the OP calls "vector of keys" or vec_of_vals) and the just processed gradeCollection.

In order to align the result object's key-value pairs (entries) with the grade-values of the already mentioned gradePrecedenceList (formerly known as vec_of_vals) one does reduce the latter, creating with each iteration an entry where the key equals the current grade-value and the value gets looked up from the before created gradeCollection by the same grade-value. In case of an unsuccessful lookup one would assign an empty array instead.

const result = gradePrecedenceList
  .reduce((collection, grade) =&gt; {

    collection[grade] = gradeCollection[grade] ?? [];
    return collection;
  }, {});

... executable example code which does prove the above said ...

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const data = [
  {grade: &quot;one&quot;},
  {grade: &quot;one&quot;},
  {grade: &quot;one&quot;},
  {grade: &quot;two&quot;},
  {grade: &quot;four&quot;},
  {grade: &quot;four&quot;},
  {grade: &quot;four&quot;},
  {grade: &quot;four&quot;},
  {grade: &quot;five&quot;},
  {grade: &quot;five&quot;},
  {grade: &quot;five&quot;},
  {grade: &quot;six&quot;}
];
const gradePrecedenceList = [&quot;one&quot;, &quot;two&quot;, &quot;three&quot;, &quot;four&quot;];

const gradeCollection = data
  .reduce((collection, dataItem) =&gt; {

    const { grade } = dataItem;
    (collection[grade] ??= []).push(dataItem);

    return collection;
  }, {});

const result = gradePrecedenceList
  .reduce((collection, grade) =&gt; {

    collection[grade] = gradeCollection[grade] ?? [];
    return collection;
  }, {});

console.log({ result });
console.log({ gradeCollection });

<!-- language: lang-css -->

.as-console-wrapper { min-height: 100%!important; top: 0; }

<!-- end snippet -->

And out of interest and for fun Alexander Nenashev's benchmark test running additionally the above implementation of "reduce twice" ...

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-html -->

&lt;script benchmark data-count=&quot;10&quot;&gt;

    const grades = &#39;abcd&#39;.split(&#39;&#39;);

    const data = JSON.parse(JSON.stringify(Array.from({ length: 300000 }).reduce(arr =&gt; arr.push(...[
        { grade: &quot;a&quot; }, { grade: &quot;a&quot; }, { grade: &quot;a&quot; }, { grade: &quot;b&quot; }, { grade: &quot;d&quot; }, { grade: &quot;d&quot; }, { grade: &quot;d&quot; }, { grade: &quot;d&quot; }, { grade: &quot;e&quot; }, { grade: &quot;e&quot; }, { grade: &quot;e&quot; }, { grade: &quot;f&quot; }
    ]) &amp;&amp; arr, [])));

    // @benchmark reducing grades and filtering data

    grades.reduce((grouped, key) =&gt; (grouped[key] = data.filter(({ grade }) =&gt; grade === key)) &amp;&amp; grouped, {});

    // @benchmark iterating data array

    const grouped = grades.reduce((grouped, grade) =&gt; (grouped[grade] = []) &amp;&amp; grouped, {});

    data.forEach(item =&gt; grouped[item.grade]?.push(item));
    grouped;

    // @benchmark reduce twice

    const gradeCollection = data
      .reduce((collection, dataItem) =&gt; {

        const { grade } = dataItem;
        (collection[grade] ??= []).push(dataItem);

        return collection;
      }, {});

    grades
      .reduce((collection, grade) =&gt; {

        collection[grade] = gradeCollection[grade] ?? [];
        return collection;
      }, {});

&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js&quot;&gt;&lt;/script&gt;

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年6月22日 06:44:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76527589.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定