假设我们有一组值:
[5, 5, 3, 5, 3, 3]
按价值和邻接对它们进行分组的最佳方法是什么。结果应如下:
[ [5,5], [3], [5], [3,3] ]
当然,我可以遍历源数组并查找下一个/上一个项目,如果它们相同,则将它们推送到一个临时数组,然后将其推送到生成的数组。
但我喜欢以功能方式编写代码。那么也许有更好的方法呢?
答案 0 :(得分:5)
您可以使用Array.prototype.reduce
方法:
var result = [5, 5, 3, 5, 3, 3].reduce(function(prev, curr) {
if (prev.length && curr === prev[prev.length - 1][0]) {
prev[prev.length - 1].push(curr);
}
else {
prev.push([curr]);
}
return prev;
}, []);
alert( JSON.stringify(result) );

答案 1 :(得分:1)
如果你在谈论性能,n - 复杂性[O(n)]就是你得到的(你正在讨论的一次迭代)。
如果您正在谈论内存使用优化,可能您需要推送/弹出对象(而不是复制它们)。或者尝试在首次创建时将输入数组组织为数组数组(例如[[5],[5],[3],[5],[3],[3]])并且只对此工作数组,将其塑造成你想要的最终形式。
如果您想在实现中更加花哨,可以尝试实现递归函数来处理它(但作为一般经验法则,在两个提供相同性能的实现之间,那个更容易阅读的是你应该瞄准的那个。)
答案 2 :(得分:1)
我也需要相同的东西,但是需要根据每个值的功能(而不是值本身)进行分组。例如,
[1, 3, 5, 2, 4, 6, 7, 8, 9, 10, 42]
成为
[[1, 3, 5], [2, 4, 6], [7], [8], [9], [10, 42]]
具有分组功能
function (a) { return a % 2; }
一种方法是扩展@dfsq的方法,但使用按字典顺序定义的fn_prev
以避免重新计算先前的fn(val)
。
var fn_prev = null;
myArray.reduce((acc, val) => {
let fn_curr = fn(val);
if (!acc.length || fn_curr !== fn_prev) {
acc.push([val]);
} else {
acc[acc.length - 1].push(val);
}
fn_prev = fn_curr;
return acc;
}, []);
我觉得避免.reduce
更为简单:
let fn_prev = null;
let acc = [];
for (let val of myArray) {
let fn_curr = fn(val);
if (!acc.length || fn_curr !== fn_prev) {
acc.push([val]);
} else {
acc[acc.length - 1].push(val);
}
fn_prev = fn_curr;
}
console.log(acc);
可以像这样将其添加到Array原型中:
Object.defineProperty(Array.prototype, 'groupByAdjacent', {
value: function(fn) {
let fn_prev = null;
let acc = [];
for (let val of [].concat(this)) {
let fn_curr = fn(val);
if (!acc.length || fn_curr !== fn_prev) {
acc.push([val]);
} else {
acc[acc.length - 1].push(val);
}
fn_prev = fn_curr;
}
return acc;
}
});
并以此方式使用:
let x = [1, 3, 5, 2, 4, 6, 7, 8, 9, 10, 42];
let grouped_x = x.groupByAdjacent(a => a % 2);
console.log(grouped_x);
答案 3 :(得分:1)
对于罕见的Array.prototype.reduceRight
-
const data =
[ 5, 5, 3, 5, 3, 3 ]
const groupAdjacent = (a = []) =>
a.reduceRight
( ([ group = [], ...result ], v) =>
group.length
? group[0] === v
? [ [ v, ...group ], ...result ] // v matches group
: [ [ v ], group, ...result ] // v does not match group
: [ [ v ], ...result ] // no group to compare
, [[]]
)
console.log(groupAdjacent(data))
// [ [ 5, 5 ], [ 3 ], [ 5 ], [ 3, 3 ] ]
console.log(groupAdjacent([]))
// [ [] ]
可以使用延续传递样式来描述相同的过程-
const data =
[ 5, 5, 3, 5, 3, 3 ]
const identity = x =>
x
const None =
Symbol ()
const groupAdjacent = ([ v = None, ...more ], k = identity) =>
v === None
? k ([[]])
: groupAdjacent (more, ([ group, ...result ]) =>
group.length
? group[0] === v
? k ([ [ v, ...group ], ...result ]) // v matches group
: k ([ [ v ], group, ...result ]) // v does not match group
: k ([ [ v ], ...result ]) // no group to compare
)
console.log(groupAdjacent(data))
// [ [ 5, 5 ], [ 3 ], [ 5 ], [ 3, 3 ] ]
console.log(groupAdjacent([]))
// [ [] ]