我想知道使用JavaScript将数组拆分成两个不同数组的最佳方法是什么,但要将其保留在函数式编程领域。
让我们说应该根据某些逻辑创建两个数组。例如,拆分一个数组应该只包含少于四个字符的字符串,另一个包含剩下的字符串。
const arr = ['horse', 'elephant', 'dog', 'crocodile', 'cat'];
我考虑过不同的方法:
过滤:
const lessThanFour = arr.filter((animal) => {
return animal.length < 4;
});
const fourAndMore = arr.filter((animal) => {
return animal.length >= 4;
});
对我来说,问题在于你必须两次检查数据,但它非常易读。如果你有一个相当大的阵列,会有两次这样的巨大影响吗?
减少:
const threeFourArr = arr.reduce((animArr, animal) => {
if (animal.length < 4) {
return [[...animArr[0], animal], animArr[1]];
} else {
return [animArr[0], [...animArr[1], animal]];
}
}, [[], []]);
数组的0索引包含少于4的数组,1索引包含超过3的数组。
我不太喜欢这个,因为看起来数据结构会产生一些问题,因为它是一个数组数组。我已经考虑过使用reduce构建一个对象,但是我无法想象它会比数组解决方案中的数组更好。
我设法在线查看类似的问题以及Stack Overflow,但其中许多都使用push()
打破了不可变性的想法,或者他们有非常难以理解的实现,我认为这打破了函数式编程的表现力。
还有其他方法吗? (功能当然)
答案 0 :(得分:8)
您尝试构建的功能通常称为partition
,可以在许多库中以该名称找到,例如underscore.js。 (据我所知,它不是内置方法)
var threeFourArr = _.partition(animals, function(x){ return x.length < 4 });
我不太喜欢这个,因为看起来数据结构会产生一些问题,看到它是一个数组数组
嗯,这是在Javascript中使用函数返回两个不同值的唯一方法。如果您可以使用destructuring assignment(ES6功能),它看起来会更好一点:
var [smalls, bigs] = _.partition(animals, function(x){ return x.length < 4 });
将其视为返回一对数组而不是返回数组数组。 &#34;阵列阵列&#34;建议你可能有不同数量的数组。
我设法在线查看类似的问题以及Stack Overflow,但是其中许多通过使用push()打破了不可变性的想法,或者他们有非常难以理解的实现,在我看来这打破了表达性函数式编程。
如果在单个函数中对其进行本地化,则可变性不是问题。从外面看它和以前一样不变,有时使用一些可变性比试图以纯函数方式做所有事情更加惯用。如果我必须从头开始编写分区函数,我会按照以下几行写一些东西:
function partition(xs, pred){
var trues = [];
var falses = [];
xs.forEach(function(x){
if(pred(x)){
trues.push(x);
}else{
falses.push(x);
}
});
return [trues, falses];
}
答案 1 :(得分:7)
collateBy
我刚刚分享了similar answer here
我更喜欢这个解决方案,因为它抽象了整理,但允许您使用高阶函数控制如何整理项目。
请注意我们如何对animal.length
内的< 4
或animals[0].push
或collateBy
进行任何说明。此过程不了解您可能正在整理的种数据。
// generic collation procedure
const collateBy = f => g => xs => {
return xs.reduce((m,x) => {
let v = f(x)
return m.set(v, g(m.get(v), x))
}, new Map())
}
// custom collator
const collateByStrLen4 =
// collate by length > 4 using array concatenation for like elements
// note i'm using `[]` as the "seed" value for the empty collation
collateBy (x=> x.length > 4) ((a=[],b)=> [...a,b])
// sample data
const arr = ['horse','elephant','dog','crocodile','cat']
// get collation
let collation = collateByStrLen4 (arr)
// output specific collation keys
console.log('greater than 4', collation.get(true))
console.log('not greater than 4', collation.get(false))
// output entire collation
console.log('all entries', Array.from(collation.entries()))
查看other answer I posted以查看其他使用类型。这是一个非常方便的程序。
bifilter
这是另一种捕获过滤函数输出的解决方案,而不是像Array.prototype.filter
那样丢弃过滤后的值。
这基本上是您的reduce
实现所做的,但它被抽象为一个通用的参数化过程。 不使用Array.prototype.push
但是在闭包体中,通常认为局部变异为OK。
const bifilter = (f,xs) => {
return xs.reduce(([T,F], x, i, arr)=> {
if (f(x, i, arr) === false)
return [T, [...F,x]]
else
return [[...T,x] ,F]
}, [[],[]])
}
const arr = ['horse','elephant','dog','crocodile','cat']
let [truthy,falsy] = bifilter(x=> x.length > 4, arr)
console.log('greater than 4', truthy)
console.log('not greater than 4', falsy)
虽然它可能稍微简单一点,但它并不像collateBy
那么强大。无论哪种方式,选择你喜欢的任何一个,根据需要调整它以满足你的需求,并且玩得开心!
如果这是您自己的应用,请坚持并将其添加到Array.prototype
// attach to Array.prototype if this is your own app
// do NOT do this if this is part of a lib that others will inherit
Array.prototype.bifilter = function(f) {
return bifilter(f,this)
}
答案 2 :(得分:3)
较短的.reduce()
版本将是:
const split = arr.reduce((animArr, animal) => {
animArr[animal.length < 4 ? 0 : 1].push(animal);
return animArr
}, [ [], [] ]);
可能与解构相结合:
const [ lessThanFour, fourAndMore ] = arr.reduce(...)
答案 3 :(得分:2)
如果您不反对使用下划线,那么有一个简洁的小功能groupBy可以完全满足您的要求:
const arr = ['horse', 'elephant', 'dog', 'crocodile', 'cat'];
var results = _.groupBy(arr, function(cur) {
return cur.length > 4;
});
const greaterThanFour = results.true;
const lessThanFour = results.false;
console.log(greaterThanFour); // ["horse", "elephant", "crocodile"]
console.log(lessThanFour); // ["dog", "cat"]
答案 4 :(得分:0)
感谢用户的漂亮回复,谢谢,这里有一个使用递归的替代方法,
const arr = ['horse', 'elephant', 'dog', 'crocodile', 'cat'];
const splitBy = predicate => {
return x = (input, a, b) => {
if (input.length > 0) {
const value = input[0];
const [z, y] = predicate(value) ? [[...a, value], b] : [a, [...b, value]];
return x(input.slice(1), z, y);
} else {
return [a, b];
}
}
}
const splitAt4 = splitBy(x => x.length < 4);
const [lessThan4, fourAndMore ] = splitAt4(arr, [], []);
console.log(lessThan4, fourAndMore);
答案 5 :(得分:-1)
我认为除了返回数组数组或包含数组的对象之外,还有其他解决方案。 javascript函数在拆分后如何返回多个数组?
编写一个包含推送逻辑的函数以便于阅读。
var myArr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var x = split(myArr, v => (v <= 5));
console.log(x);
function split(array, tester) {
const result = [
[],
[]
];
array.forEach((v, i, a) => {
if (tester(v, i, a)) result[0].push(v);
else result[1].push(v);
});
return result;
}