const task = [
{ x: 1, y: 20 },
{ x: 2, y: 30 },
{ x: 1, y: 50 },
{ x: 2, y: 20 },
{ x: 1, y: 10 },
{ x: 9, y: 40 },
{ x: 1, y: 30 },
{ x: 3, y: 5 }
];
有两个条件:
例如z=2
和t=60
(我将这些值作为参数传递给函数),结果应为:
let result = [
[{ x: 1, y: 20 },{ x: 2, y: 30 }],
[{ x: 1, y: 50 }],
[{ x: 2, y: 20 },{ x: 1, y: 10 }],
[{ x: 9, y: 40 }],
[{ x: 1, y: 30 },{ x: 3, y: 5 }]
];
我已设法使用for
循环实现,但我对可能的功能解决方案感到好奇。任何帮助都会得到满足。
答案 0 :(得分:4)
稍微更具功能性的方法将包括一些非常简单的纯帮助程序。让我们以函数ySum
开头,它返回给定参数y
中所有group
属性的总和:
const ySum = (group) => group.reduce((sum, { y: currY }) => sum + currY, 0);
接下来,让我们定义一个函数,它接受一个任务和一个组,告诉我们给定的任务是否适合给定的组:
从评论中更新对于空组,此函数始终返回true,以防止在y > 60
执行特定任务时出现空数组问题
const taskFitsInGroup = (task, group) => {
if (group.length === 0) {
return true;
}
return (group.length < 2) && (ySum(group) + task.y <= 60);
};
注意:您当然可以将常量2
和60
提取到变量/参数。
Array.prototype.reduce是您需要的数据转换的理想选择。在实施addTask
之后,以下行应该满足您的要求:
const result = tasks.reduce(addTask, [[]]);
只剩下要做的事情是创建我们上面提到addTask
的{{1}}函数。使用我们已经创建的辅助函数,这实际上非常简单:
reduce
工作示例:
const addTask = (accumulation, task) => {
const lastGroup = accumulation[accumulation.length - 1];
if (taskFitsInGroup(task, lastGroup)) {
lastGroup.push(task);
} else {
accumulation.push([task]);
}
return accumulation;
};
&#13;
答案 1 :(得分:3)
这是另一个功能性的ES6解决方案:
function chunk(task, z, t) {
return task.reduce( ({sum, arr, i}, {y}, j) =>
(j > i && sum + y > t) || j - i >= z
? {sum: y, arr: [...arr, arr[arr.length-1].splice(j-i)], i: j}
: {sum: sum + y, arr, i}
, {sum: 0, arr: [task.slice()], i: 0}).arr;
}
const task = [{x:1,y:20}, {x:2,y:30}, {x:1,y:50}, {x:2,y:20},{x:1,y:10},{x:9,y:40},{x:1,y:30},{x:3,y:40}];
const result = chunk(task, 2, 60);
console.log(JSON.stringify(result));
&#13;
注意:注意额外的j > i
条件,即使y
大于限制,也要确保结果中的每个子数组都至少有一个元素。否则你可能会有一个无限循环 - 永远不能放置具有太大y
- 值的元素。
该功能从创建reduce
回调的起始值开始:
{sum: 0, arr: [task.slice()], i: 0}
arr
属性表示到目前为止的结果,它是原始数组,其所有元素组合在一起。 slice
是确保我们拥有数组副本并且不会改变原始数据所必需的。{/ p>
sum
属性将跟踪最后一个块中的总和,直到reduce
循环的当前索引。
i
属性告诉原始数组中的索引将最后一个分成新块。
reduce
回调包含以下参数:
{sum, arr, i}
:这是上述对象的解构表示。 reduce
回调将始终返回具有这三个属性的对象,因此它们将在下一次迭代中再次作为回调的参数提供。{y}
:再次进行解构以提取y
数组中当前元素的task
值。j
:task
数组然后短表达式语法用于箭头函数(=>
):它后面没有语句块,只是一个表达式,它是回调的返回值。
此表达式使用三元运算符来决定当前元素是否可以保留在当前块中,在这种情况下,sum
和arr
只需更新i
属性可以保持不变。短ES6对象表示法用于表示arr
属性将获得arr
值。 i
:
: {sum: sum + y, arr, i}
另一种情况是必须拆分新块:
? {sum: y, arr: [...arr, arr[arr.length-1].splice(j-i)], i: j}
此处splice
方法用于从i-j
第一个元素之后的当前最后一个块中删除所有元素。删除的元素将放回一个新数组中,作为它的最后一个新元素。其余部分以...arr
复制。我必须承认,这不是纯粹的功能,因为arr
的最后一个子阵列正在用splice
进行变异。但是使用slice
编写相同内容需要更多代码。
决定采取哪两项措施的条件反映了您指定的条件:y > t
和j - i >= z
。我已经解释了为什么我将j > i
添加到第一个条件。
答案 2 :(得分:2)
这是使用.reduce
的一个潜在解决方案:
var task = [{x:1,y:20}, {x:2,y:30}, {x:1,y:50}, {x:2,y:20},{x:1,y:10},{x:9,y:40},{x:1,y:30},{x:3,y:40}];
// grouping limit can be max z
// sums of y can be max t
// the result should be:
// [[{x:1,y:20}, {x:2,y:30}],[{x:1,y:50}], [{x:2,y:20},{x:1,y:10}],[{x:9,y:40}],[{x:1,y:30},{x:3,y:5}]];
function sumY(arr) {
return arr.reduce((sum, item) => sum + item.y, 0);
}
function group(maxSize, maxSum, arr) {
return arr.reduce((groups, item) => {
const last = groups[groups.length - 1];
(!last || last.length >= maxSize || sumY(last) + item.y > maxSum)
? groups.push([item])
: last.push(item);
return groups;
}, []);
}
console.log(group(2, 60, task));
&#13;
答案 3 :(得分:2)
您可以为实际组使用辅助数组,并将属性y
与实际对象的y
属性相加
group.reduce((s, { y }) => s + y, o.y)
^^^^^ array
^ accumulator for sum
^^^^^ destructuring to get y property
^^^^^ build sum and return value
^^^ start accumulator with actual y prop
用于检查给定值。
function group(array, z, t) {
var result = [];
array.reduce(function (group, o, i) {
var sum = group.reduce((s, { y }) => s + y, o.y);
if (i && sum <= t && group.length < z) {
group.push(o);
} else {
result.push(group = [o]);
}
return group;
}, []);
return result;
}
var task = [{ x: 1, y: 20 }, { x: 2, y: 30 }, { x: 1, y: 50 }, { x: 2, y: 20 }, { x: 1, y: 10 }, { x: 9, y: 40 }, { x: 1, y: 30 }, { x: 3, y: 5 }],
result = group(task, 2, 60);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
答案 4 :(得分:1)
const group = (arr, z, t, result = [[]], count = 0, y = 0) =>
(arr.reduce((acc, curr) => (
count++, y += curr.y,
count > z || y >= t
? (count = 0, y = curr.y, result[result.push([curr]) - 1])
: (acc.push(curr), acc)
), result[0]), result);
这是一种纯粹的功能性方法。使用group(task, 2, 60)
进行调用。但是我仍然更喜欢旧的for循环:
const result = [];
let group = [], z = 2, t = 60, count = 0, y = 0;
for(const task of tasks){
if(count++ >= z || (y += task.y) >= t){
count = 0;
y = task.y;
result.push(group);
group = [];
}
group.push(task);
}
if(group.length) result.push(group);
答案 5 :(得分:1)
您可以使用reduce()
方法,并将一个变量用于当前数组编号,将一个变量用于当前y
和值。
const task = [{x:1,y:20}, {x:2,y:30}, {x:1,y:50}, {x:2,y:20},{x:1,y:10},{x:9,y:40},{x:1,y:30},{x:3,y:40}];
const group = (data, z, t) => {
let n = 0, tTemp = 0;
return data.reduce(function(r, e) {
if ((r[n] && r[n].length >= z) || (tTemp + e.y) > t) n += 1, tTemp = e.y;
else tTemp += e.y;
r[n] = (r[n] || []).concat(e)
return r;
}, [])
}
const result = group(task, 2, 60)
console.log(result)
答案 6 :(得分:1)
const task = [{x:1,y:20}, {x:2,y:30}, {x:1,y:50}, {x:2,y:20},{x:1,y:10},{x:9,y:40},{x:1,y:30},{x:3,y:40}],
z = 2, t = 60;
const group = task =>
task.reduce((a, e) => (
!!a.length && a[a.length - 1].length < z &&
a[a.length - 1].reduce((s, {y}) => s + y, e.y) <= t ?
a[a.length - 1].push(e) : a.push([e])
) && a, [])
console.log(JSON.stringify(group(task)))