jim*_*web 21 javascript arrays functional-programming
我正在尝试使用reduce()"整理"顺序组合一组数组,因此具有相似索引的项目在一起.例如:
input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]
output = [ 'first','1','uno','one','second','2','dos','two','third','3','three','4' ]
Run Code Online (Sandbox Code Playgroud)
只要它们在一起,具有相似索引的项目的顺序无关紧要,因此结果'one','uno','1'...是如上所述.我想尽可能使用不可变变量.
我有办法:
const output = input.reduce((accumulator, currentArray, arrayIndex)=>{
currentArray.forEach((item,itemIndex)=>{
const newIndex = itemIndex*(arrayIndex+1);
accumulator.splice(newIndex<accumulator.length?newIndex:accumulator.length,0,item);
})
return accumulator;
})
Run Code Online (Sandbox Code Playgroud)
但它不是很漂亮而且我不喜欢它,特别是因为它在forEach方法中改变累加器的方式.我觉得必须有一个更优雅的方法.
我不敢相信之前没有人问过这个问题,但是我尝试了一堆不同的查询而无法找到它,所以请告诉我它是否在那里而我错过了它.有没有更好的办法?
为了澄清评论中的每个问题,我希望能够在不改变任何变量或数组的情况下执行此操作,因为我正在使用accumulator.splice和仅使用函数方法,例如.map,或者.reduce不使用类似的变异循环.forEach.
也许只是一个简单的for... i循环,检查每个数组的位置项目i
var input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["1st","2nd","3rd"]]
var output = []
var maxLen = Math.max(...input.map(arr => arr.length));
for (i=0; i < maxLen; i++) {
input.forEach(arr => { if (arr[i] !== undefined) output.push(arr[i]) })
}
console.log(output)Run Code Online (Sandbox Code Playgroud)
简单,但可预测和可读
如果你需要避免forEach,这里有一个类似的方法,你可以:获取最大子数组长度,构建一个由for循环([1,2,3,4])创建的整数范围,映射每个值以旋转数组,展平多个-dimensional数组,然后过滤掉空单元格.
首先是不连续的步骤,然后是一个衬里:
var input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["1st","2nd","3rd"]];
Run Code Online (Sandbox Code Playgroud)
多个步骤:
var maxLen = Math.max(...input.map(arr => arr.length));
var indexes = Array(maxLen).fill().map((_,i) => i);
var pivoted = indexes.map(i => input.map(arr => arr[i] ));
var flattened = pivoted.flat().filter(el => el !== undefined);
Run Code Online (Sandbox Code Playgroud)
一个班轮:
var output = Array(Math.max(...input.map(arr => arr.length))).fill().map((_,i) => i)
.map(i => input.map(arr => arr[i] ))
.flat().filter(el => el !== undefined)
Run Code Online (Sandbox Code Playgroud)
使用Array.from()创建最长子数组的长度的新数组.要获取最长子数组的长度,请获取长度数组Array.map()并使用最大项.
在回调中Array.from()使用Array.reduceRight()或Array.reduce()(根据您想要的顺序)从每个子数组中收集项目.如果子数组中存在当前索引,则取项.用Array.flat().展平子数组.
const input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]
const result = Array.from(
{ length: Math.max(...input.map(o => o.length)) },
(_, i) => input.reduceRight((r, o) =>
i < o.length ? [...r, o[i]] : r
, [])
)
.flat();
console.log(result);Run Code Online (Sandbox Code Playgroud)
我使用递归方法来避免突变。
let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]
function recursion(input, idx = 0) {
let tmp = input.map(elm => elm[idx])
.filter(e => e !== undefined)
return tmp[0] ? tmp.concat(recursion(input, idx + 1)) : []
}
console.log(recursion(input))Run Code Online (Sandbox Code Playgroud)
这是一个符合您指定的优雅标准的递归解决方案:
const head = xs => xs[0];
const tail = xs => xs.slice(1);
const notNull = xs => xs.length > 0;
console.log(collate([ ["one", "two", "three"]
, ["uno", "dos"]
, ["1", "2", "3", "4"]
, ["first", "second", "third"]
]));
function collate(xss) {
if (xss.length === 0) return [];
const yss = xss.filter(notNull);
return yss.map(head).concat(collate(yss.map(tail)));
}Run Code Online (Sandbox Code Playgroud)
它可以直接翻译成Haskell代码:
collate :: [[a]] -> [a]
collate [] = []
collate xss = let yss = filter (not . null) xss
in map head yss ++ collate (map tail yss)
Run Code Online (Sandbox Code Playgroud)
先前的解决方案采取了重大步骤来计算答案。这是一个递归解决方案,它需要一些小步骤来计算答案:
console.log(collate([ ["one", "two", "three"]
, ["uno", "dos"]
, ["1", "2", "3", "4"]
, ["first", "second", "third"]
]));
function collate(xss_) {
if (xss_.length === 0) return [];
const [xs_, ...xss] = xss_;
if (xs_.length === 0) return collate(xss);
const [x, ...xs] = xs_;
return [x, ...collate(xss.concat([xs]))];
}Run Code Online (Sandbox Code Playgroud)
以下是等效的Haskell代码:
collate :: [[a]] -> [a]
collate [] = []
collate ([]:xss) = collate xss
collate ((x:xs):xss) = x : collate (xss ++ [xs])
Run Code Online (Sandbox Code Playgroud)
希望能有所帮助。
我见过名为round-robin的问题,但也许interleave是一个更好的名字。当然,map、reduce、 和filter是函数式程序,但并非所有函数式程序都需要依赖它们。当这些是我们唯一知道如何使用的函数时,生成的程序有时会很尴尬,因为通常有更合适的函数。
map产生一对一的结果。如果我们有 4 个子数组,我们的结果将有 4 个元素。interleave应该产生一个等于组合子数组长度的结果,所以map只能让我们到达那里。需要额外的步骤才能获得最终结果。
reduce一次一个地迭代输入元素以产生最终结果。在第一次归约中,我们将获得第一个子数组,但在进入下一个子数组之前没有直接的方法来处理整个子数组。我们可以强制我们的程序使用reduce,但这样做会让我们将排序过程视为一个递归过程,而不是它实际上是什么。
事实上,您并不局限于使用这些原始函数过程。您可以interleave采用直接编码其意图的方式进行编写。我认为 ainterleave有一个漂亮的递归定义。我认为这里使用深度解构赋值很好,因为函数的签名显示了期望的数据的形状interleave;数组的数组。数学归纳法使我们能够自然地处理程序的分支 -
const None =
Symbol ('None')
const interleave =
( [ [ v = None, ...vs ] = [] // first subarray
, ...rest // rest of subarrays
]
) =>
v === None
? rest.length === 0
? vs // base: no `v`, no `rest`
: interleave (rest) // inductive: some `rest`
: [ v, ...interleave ([ ...rest, vs ]) ] // inductive: some `v`, some `rest`
const input =
[ [ "one", "two", "three" ]
, [ "uno", "dos" ]
, [ "1", "2", "3", "4" ]
, [ "first", "second", "third" ]
]
console.log (interleave (input))
// [ "one", "uno", "1", "first", "two", "dos", "2", "second", "three", "3", "third", "4" ]Run Code Online (Sandbox Code Playgroud)
interleave让我们摆脱了狭隘思维的束缚。我不再需要根据那些笨拙地组合在一起的畸形部分来考虑我的问题 - 我不再考虑数组索引、 or sort、 or forEach、或使用 改变状态push,或使用>or进行比较Math.max。我也不必考虑像数组这样反常的事情——哇,我们确实认为我们对 JavaScript 的了解是理所当然的!
上面,没有依赖关系应该让人感觉耳目一新。想象一下初学者接触这个程序:他/她只需要学习 1) 如何定义函数,2) 解构语法,3) 三元表达式。由无数小依赖项拼凑在一起的程序将要求学习者熟悉每个依赖项,然后才能获得对该程序的直觉。
也就是说,用于解构值的 JavaScript 语法并不是最漂亮的,有时会为了提高可读性而牺牲便利性 -
const interleave = ([ v, ...vs ], acc = []) =>
v === undefined
? acc
: isEmpty (v)
? interleave (vs, acc)
: interleave
( [ ...vs, tail (v) ]
, [ ...acc, head (v) ]
)
Run Code Online (Sandbox Code Playgroud)
这里演变的依赖关系是isEmpty、tail和head-
const isEmpty = xs =>
xs.length === 0
const head = ([ x, ...xs ]) =>
x
const tail = ([ x, ...xs ]) =>
xs
Run Code Online (Sandbox Code Playgroud)
功能是一样的——
const input =
[ [ "one", "two", "three" ]
, [ "uno", "dos" ]
, [ "1", "2", "3", "4" ]
, [ "first", "second", "third" ]
]
console.log (interleave (input))
// [ "one", "uno", "1", "first", "two", "dos", "2", "second", "three", "3", "third", "4" ]
Run Code Online (Sandbox Code Playgroud)
在您自己的浏览器中验证以下结果 -
const interleave = ([ v, ...vs ], acc = []) =>
v === undefined
? acc
: isEmpty (v)
? interleave (vs, acc)
: interleave
( [ ...vs, tail (v) ]
, [ ...acc, head (v) ]
)
Run Code Online (Sandbox Code Playgroud)
如果您开始考虑interleave使用map、filter和reduce,那么它们很可能会成为最终解决方案的一部分。如果这是您的方法,您应该会感到惊讶map,在这个答案的两个程序中看不到 、filter、 和。reduce这里的教训是你会成为你所知道的的囚徒。有时您需要忘记map并reduce观察其他问题具有独特的性质,因此通用方法虽然可能有效,但不一定是最合适的。
| 归档时间: |
|
| 查看次数: |
507 次 |
| 最近记录: |