【问题标题】:javascript split an object of arrays into an array of objects given a sizejavascript将数组对象拆分为给定大小的对象数组
【发布时间】:2021-05-22 21:42:23
【问题描述】:

我有一个具有以下属性的对象:

const rows_obj = {
    prova1:[{price:3,description:"test11"},{price:7,description:"test12"}],
    prova2:[{price:11,description:"test21"},{price:2,description:"test22"}],
    prova3:[{price:1,description:"test31"},{price:23,description:"test32"}],
}

并且我需要在一页或多页中打印行,因此每页的限制例如为 3 行。所以在这种情况下,我需要一个对象 obj 数组,例如:

obj[0] = {
        total:21,
        prova1:[{price:3,description:"test11"},{price:7,description:"test12"},],
        prova2:[{price:11,description:"test21"}],
    }

obj[1] = {
        total:26,
        prova2:[{price:2,description:"test22"}],
        prova3:[{price:1,description:"test31"},{price:23,description:"test32"},],
    }

(因为在这种情况下,限制是每页/对象 3 行)

但限制也可能是 20 行,因此最终对象将是:

obj[0] = {
        total:47,
        prova1:[{price:3,description:"test11"},{price:7,description:"test12"},],
        prova2:[{price:11,description:"test21"},{price:2,description:"test22"},],
        prova3:[{price:1,description:"test31"},{price:23,description:"test32"},],
    }

因为在原始对象中有6行,那么,由于在限制之下,函数必须检索一个包含一个元素的数组,并且这个元素等于原始的那个。

我试过了,但到目前为止我已经编写了这个代码:

const size = 3
const rows_obj = {
    prova1:[{price:22,description:"test11"},{price:23,description:"test12"},],
    prova2:[{price:22,description:"test21"},{price:23,description:"test22"},],
    prova3:[{price:22,description:"test31"},{price:23,description:"test32"},],
}

var rows_length = 0;

for(var char in rows_obj){
  // Confirm that the key value is an array before adding the value.
  if(Array.isArray(rows_obj[char])){
    rows_length += rows_obj[char].length;   
  }
}

  if (!rows_length) {
    return [[]]
  }

  const arrays = []
  let i = 0

  const keys = Object.keys(rows_obj)
  let obj = null
  
  while (i<rows_length) {
    obj = {}
    for(let j=0;j<keys.length;j++) {
      obj[keys[j]] = rows_obj[keys[j]].slice(i, i + size)
      i = i + 2 + size
      console.log(i)
    } 
    arrays.push(obj)
  }

它不工作,我做的一团糟......有什么帮助吗?提前谢谢你。

【问题讨论】:

标签: javascript algorithm


【解决方案1】:

我觉得这不是最好的解决方案,但可以按您的预期工作。

const rows_obj = {
    prova1:[{price:22,description:"test11"},{price:23,description:"test12"},],
    prova2:[{price:22,description:"test21"},{price:23,description:"test22"},],
    prova3:[{price:22,description:"test31"},{price:23,description:"test32"},],
}

const rows = Object.entries(rows_obj).flatMap( ([k, v]) => (
  v.map(e => ({ key: k, ...e }) )
))

const size = 3
const groups = []
let i = 0

while(i < rows.length) {
  groups.push(rows.slice(i, i + size))
  i += size
}

const res = groups.map(e => e.reduce((acc, {key, ...rest}) => {
  acc[key]
    ? acc[key].push({...rest})
    : acc[key] = [{...rest}]
  return acc
}, {}))

console.log(res)
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

  • 感谢@ulou 的回复。既然你这么好心回答这个问题,我可以问你,如果我想为每个子对象 obj 添加总价格,比如 obj[0] = { 'prova1':[array], 'prova2':[ array],...,total:67 } 因为是 22+23+22。 (因为第二个 obj 是 68)。谢谢
  • @July 在return acc 上方添加acc.total += rest.price 并将其下方的行从}, {})) 更改为}, {total: 0})),它应该可以按预期工作。
  • 您已经询问过“新要求”,我在上面的评论中回复了您如何做到这一点。 SO 社区在这里帮助您解决问题,而不是代替您做作业或任务,每次当您(来自某人?)有新要求时更改已接受的答案并更新您的预期输出不是它的工作原理。跨度>
  • 我真的很抱歉 ulou,我已经编辑了这个问题,因为如果你输入像 20 这样的大小,它就不起作用,我写道:(因为在这种情况下,限制是每页 3 行/目的) 。但那是因为我解释错了,限制可能更高,所以我编辑了这个问题。没有公认的答案,因为我解释错了。感谢您的理解,祝您有美好的一天,感谢您的帮助。
  • 你说它不起作用是什么意思?如果你输入大小 20(或任何其他正数),它会按预期工作。
【解决方案2】:

更新

从问题更新中添加附加要求的总和并不难:

const regroup = (n) => (xs) => 
  chunk (n) (Object .entries (xs) .flatMap (([k, xs]) => xs .map (x => [k, x])))
    .map (group => group .reduce (
      ({total, ...r}, [k, v]) => ({
        total: total + v.price, 
        ...r, 
        [k]: (r [k] || []) .concat (v)
      }), {total: 0}
    ))

你可以通过展开这个 sn-p 看到这一点:

const chunk = (n) => (xs) => 
  xs .length < n ? [[...xs]] : [xs .slice (0, n), ...chunk (n) (xs. slice(n))]

const regroup = (n) => (xs) => 
  chunk (n) (Object .entries (xs) .flatMap (([k, xs]) => xs .map (x => [k, x])))
    .map (group => group .reduce (
      ({total, ...r}, [k, v]) => ({total: total + v.price, ...r, [k]: (r [k] || []) .concat (v)}), 
      {total: 0}
    ))

const rows_obj = {prova1: [{price: 3, description: "test11"}, {price: 7, description: "test12"}], prova2: [{price: 11, description: "test21"}, {price: 2, description: "test22"}], prova3: [{price: 1, description: "test31"}, {price: 23, description: "test32"}]}


console .log ('3', regroup (3) (rows_obj))
console .log ('4', regroup (4) (rows_obj))
console .log ('5', regroup (5) (rows_obj))
.as-console-wrapper {max-height: 100% !important; top: 0}

原答案

这个版本使用了一个辅助函数chunk,它将一个数组分成给定长度的块(加上可能的剩余部分)。所以chunk (3) ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) //=&gt; [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]。它对输入进行了几次简单的转换,最后一个是您的目标:

const chunk = (n) => (xs) => 
  xs .length < n ? [[...xs]] : [xs .slice (0, n), ...chunk (n) (xs. slice(n))]

const regroup = (n) => (xs) => 
  chunk (n) (Object .entries (xs) .flatMap (([k, xs]) => xs .map (x => [k, x])))
    .map (
      group => group .reduce ((a, [k, v]) => ({...a, [k]: (a [k] || []) .concat (v)}), 
      {}
    ))

const rows_obj = {prova1: [{price: 22, description: "test11"}, {price: 23, description: "test12"}], prova2: [{price: 22, description: "test21"}, {price: 23, description: "test22"}], prova3: [{price: 22, description: "test31"}, {price: 23, description: "test32"}]}

console .log (regroup (3) (rows_obj))
.as-console-wrapper {max-height: 100% !important; top: 0}

Object .entries (xs) .flatMap (...) 之后,我们得到

[
  ["prova1", {description: "test11", price: 22}],
  ["prova1", {description: "test12", price: 23}],
  ["prova2", {description: "test21", price: 22}],
  ["prova2", {description: "test22", price: 23}],
  ["prova3", {description: "test31", price: 22}],
  ["prova3", {description: "test32", price: 23}],
]

然后调用chunk (3)我们得到

[
  [
    ["prova1", {description: "test11", price: 22}],
    ["prova1", {description: "test12", price: 23}],
    ["prova2", {description: "test21", price: 22}],
  ], [
    ["prova2", {description: "test22", price: 23}],
    ["prova3", {description: "test31", price: 22}],
    ["prova3", {description: "test32", price: 23}],
  ]
]

最后,map (group =&gt; group.reduce (...)),我们结束了

[
  {
    prova1: [
      {description: "test11", price: 22},
      {description: "test12", price: 23}
    ],
    prova2: [
      {description: "test21", price: 22}
    ]
  },
  {
    prova2: [
      {description: "test22", price: 23}
    ],
    prova3: [
      {description: "test31", price: 22},
      {description: "test32", price: 23}
    ]
  }
]

但在我看来,您的要求和输入数据结构之间存在根本的不匹配。当你从一个对象开始时,你是从一个基本无序的集合开始的。虽然现代 JS 确实定义了它的迭代顺序,但对象仍然不是用于有序集合的正确结构。我建议这可能会更好:

const rows_obj = [
  {prova1: [{price: 22, description: "test11"}, {price: 23, description: "test12"}]},
  {prova2: [{price: 22, description: "test21"}, {price: 23, description: "test22"}]},
  {prova3: [{price: 22, description: "test31"}, {price: 23, description: "test32"}]}
]

更改此代码以处理该结构应该很简单。

【讨论】:

  • 请验证已编辑的问题@Scott Sauyet。非常感谢
  • 我能从您的角度问一下,当您将值设置为大于 3 时,它是否有效?非常感谢
  • 抱歉,chunk 中有错字。现在已经修好了。这将教会我重新键入一个函数而不是粘贴它,然后不认真地测试它。
【解决方案3】:

使用Object#entriesArray#reduceArray#forEach

const 
  rows_obj = {
    prova1:[{price:3,description:"test11"},{price:7,description:"test12"}],
    prova2:[{price:11,description:"test21"},{price:2,description:"test22"}],
    prova3:[{price:1,description:"test31"},{price:23,description:"test32"}],
  },
  SIZE = 3;
let count = 0; // count of items in last object

const rowEntries = Object.entries(rows_obj);
// iterate over rows_obj entries while updating finalList
const res = rowEntries.reduce((finalList, [currentKey, currentItems]) => {
  // iterate over current items
  currentItems.forEach(item => {
    // if SIZE is reached in the last object, add new one
    if(count === SIZE) {
      count = 0;
      finalList.push({ total: 0 });
    }
    // update last object
    const last = finalList[finalList.length-1];
    finalList[finalList.length-1] = {
      ...last,
      total:        last.total + item.price,
      [currentKey]: [...(last[currentKey] || []), item]
    };
    count++;
  });
  return finalList;
}, rowEntries.length > 0 ? [{ total: 0 }] : []);

console.log(res);

【讨论】:

  • 感谢 Majed 的回复。对此,我真的非常感激。但我需要一个对象数组,而不是具有索引属性的对象
  • 请验证已编辑的问题@Majed Badawi。非常感谢
  • @July:将这种索引对象转换为数组非常容易:Object.assign ([], indexedObject)
  • @July 我更新了答案以返回带有新设计的列表
猜你喜欢
  • 2015-10-16
  • 2011-02-23
  • 2021-09-14
  • 1970-01-01
  • 2022-01-22
  • 2016-09-23
  • 1970-01-01
  • 1970-01-01
  • 2021-12-13
相关资源
最近更新 更多