使用函数 JavaScript 将数组拆分为两个不同的数组
Split array into two different arrays using functional JavaScript
我想知道使用 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 索引包含少于四的数组,1 索引包含多于三的数组。
不太喜欢这个,因为好像数据结构要出点问题,看是一个数组的数组。我考虑过使用 reduce 构建一个对象,但我无法想象它会比数组解决方案中的数组更好。
我设法在网上查看了类似的问题以及 Stack Overflow,但是其中许多问题通过使用 push()
打破了不变性的想法,或者它们的实现非常难以理解,在我看来这打破了函数式编程的表现力。
还有其他方法吗? (当然是功能性的)
如果您不反对使用下划线,那么有一个名为 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"]
您尝试构建的函数通常称为 partition
,并且可以在许多库中以该名称找到,例如 underscore.js。 (据我所知这不是内置方法)
var threeFourArr = _.partition(animals, function(x){ return x.length < 4 });
I don't like this too much, because it seems that the data structure is going to give a bit of problems, seeing that it is an array of arrays
嗯,这是在 Javascript 中拥有一个 returns 两个不同值的函数的唯一方法。如果可以使用 destructuring assignment(ES6 功能),它看起来会好一些:
var [smalls, bigs] = _.partition(animals, function(x){ return x.length < 4 });
将其视为返回一对数组而不是返回数组的数组。 "Array of arrays" 建议您可以使用可变数量的数组。
I've managed to look at similar questions online as well as Stack Overflow, but many of these break the idea of immutability by using push() or they have very unreadable implementations, which in my opinion breaks the expressiveness of functional programming.
如果将可变性本地化到单个函数中,则可变性不是问题。从外部看,它和以前一样不可变,有时使用一些可变性比尝试以纯函数方式做所有事情更符合惯用方式。如果我必须从头开始编写分区函数,我会按照以下几行写一些东西:
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];
}
我认为除了 return 数组数组或包含数组的对象之外没有其他解决方案。拆分后 javascript 函数 return 多个数组还有什么不同?
编写包含推送逻辑的函数以提高可读性。
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;
}
更短的 .reduce()
版本是:
const split = arr.reduce((animArr, animal) => {
animArr[animal.length < 4 ? 0 : 1].push(animal);
return animArr
}, [ [], [] ]);
可能与解构相结合:
const [ lessThanFour, fourAndMore ] = arr.reduce(...)
collateBy
我刚刚分享了一个similar answer here
我更喜欢这个解决方案,因为它抽象了整理,但允许您控制如何使用高阶函数整理项。
请注意我们如何在 collateBy
中不提及 animal.length
或 < 4
或 animals[0].push
。此过程不知道您可能正在整理的 种类 数据。
// 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)
}
感谢用户的漂亮回复谢谢,这里是使用递归的替代方法,
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);
我想知道使用 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 索引包含少于四的数组,1 索引包含多于三的数组。
不太喜欢这个,因为好像数据结构要出点问题,看是一个数组的数组。我考虑过使用 reduce 构建一个对象,但我无法想象它会比数组解决方案中的数组更好。
我设法在网上查看了类似的问题以及 Stack Overflow,但是其中许多问题通过使用 push()
打破了不变性的想法,或者它们的实现非常难以理解,在我看来这打破了函数式编程的表现力。
还有其他方法吗? (当然是功能性的)
如果您不反对使用下划线,那么有一个名为 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"]
您尝试构建的函数通常称为 partition
,并且可以在许多库中以该名称找到,例如 underscore.js。 (据我所知这不是内置方法)
var threeFourArr = _.partition(animals, function(x){ return x.length < 4 });
I don't like this too much, because it seems that the data structure is going to give a bit of problems, seeing that it is an array of arrays
嗯,这是在 Javascript 中拥有一个 returns 两个不同值的函数的唯一方法。如果可以使用 destructuring assignment(ES6 功能),它看起来会好一些:
var [smalls, bigs] = _.partition(animals, function(x){ return x.length < 4 });
将其视为返回一对数组而不是返回数组的数组。 "Array of arrays" 建议您可以使用可变数量的数组。
I've managed to look at similar questions online as well as Stack Overflow, but many of these break the idea of immutability by using push() or they have very unreadable implementations, which in my opinion breaks the expressiveness of functional programming.
如果将可变性本地化到单个函数中,则可变性不是问题。从外部看,它和以前一样不可变,有时使用一些可变性比尝试以纯函数方式做所有事情更符合惯用方式。如果我必须从头开始编写分区函数,我会按照以下几行写一些东西:
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];
}
我认为除了 return 数组数组或包含数组的对象之外没有其他解决方案。拆分后 javascript 函数 return 多个数组还有什么不同?
编写包含推送逻辑的函数以提高可读性。
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;
}
更短的 .reduce()
版本是:
const split = arr.reduce((animArr, animal) => {
animArr[animal.length < 4 ? 0 : 1].push(animal);
return animArr
}, [ [], [] ]);
可能与解构相结合:
const [ lessThanFour, fourAndMore ] = arr.reduce(...)
collateBy
我刚刚分享了一个similar answer here
我更喜欢这个解决方案,因为它抽象了整理,但允许您控制如何使用高阶函数整理项。
请注意我们如何在 collateBy
中不提及 animal.length
或 < 4
或 animals[0].push
。此过程不知道您可能正在整理的 种类 数据。
// 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)
}
感谢用户的漂亮回复谢谢,这里是使用递归的替代方法,
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);