reduce array of arrays into on array in collated order
I'm trying to use reduce()
combine a set of arrays in a "collated" order so items with similar indexes are together. For example:
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' ]
It doesn't matter what order the items with similar index go as long as they are together, so a result of 'one','uno','1'...
is a good as what's above. I would like to do it just using immutable variables if possible.
I have a way that works:
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;
})
But it's not very pretty and I don't like it, especially because of the way it mutates the accumulator in the forEach method. I feel there must be a more elegant method.
I can't believe no one has asked this before but I've tried a bunch of different queries and can't find it, so kindly tell me if it's there and I missed it. Is there a better way?
To clarify per question in comments, I would like to be able to do this without mutating any variables or arrays as I'm doing with the accumulator.splice
and to only use functional methods such as .map
, or .reduce
not a mutating loop like a .forEach
.
javascript arrays functional-programming
add a comment |
I'm trying to use reduce()
combine a set of arrays in a "collated" order so items with similar indexes are together. For example:
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' ]
It doesn't matter what order the items with similar index go as long as they are together, so a result of 'one','uno','1'...
is a good as what's above. I would like to do it just using immutable variables if possible.
I have a way that works:
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;
})
But it's not very pretty and I don't like it, especially because of the way it mutates the accumulator in the forEach method. I feel there must be a more elegant method.
I can't believe no one has asked this before but I've tried a bunch of different queries and can't find it, so kindly tell me if it's there and I missed it. Is there a better way?
To clarify per question in comments, I would like to be able to do this without mutating any variables or arrays as I'm doing with the accumulator.splice
and to only use functional methods such as .map
, or .reduce
not a mutating loop like a .forEach
.
javascript arrays functional-programming
"Is there a better way?" What do you mean by "better"? "I don't like it" is not an objective coding problem. What is the requirement?
– guest271314
4 hours ago
Well for one thing I'd like to do it without mutating any variables, or introducing a.forEach
loop which isn't really functional. And I think it can be done much more concisely as well. If I could zip a set of arrays together like in Python then reduce(concat) them together that way would work too.
– jimboweb
4 hours ago
I think I pretty clearly said I'd like the result to not mutate any arrays or variables, as I am doing with theaccumulator.splice
. That is an objective requirement.
– jimboweb
4 hours ago
Does the requirement include a restriction on usingJSON.parse(JSON.stringify(input))
to avoid mutating the original array?
– guest271314
3 hours ago
Wow, lots of great answers, thanks. I'm a little torn on which one to accept as a solution, so I have to try them out. I'll choose a solution in a day or so. but I voted you all up. Thanks again.
– jimboweb
38 mins ago
add a comment |
I'm trying to use reduce()
combine a set of arrays in a "collated" order so items with similar indexes are together. For example:
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' ]
It doesn't matter what order the items with similar index go as long as they are together, so a result of 'one','uno','1'...
is a good as what's above. I would like to do it just using immutable variables if possible.
I have a way that works:
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;
})
But it's not very pretty and I don't like it, especially because of the way it mutates the accumulator in the forEach method. I feel there must be a more elegant method.
I can't believe no one has asked this before but I've tried a bunch of different queries and can't find it, so kindly tell me if it's there and I missed it. Is there a better way?
To clarify per question in comments, I would like to be able to do this without mutating any variables or arrays as I'm doing with the accumulator.splice
and to only use functional methods such as .map
, or .reduce
not a mutating loop like a .forEach
.
javascript arrays functional-programming
I'm trying to use reduce()
combine a set of arrays in a "collated" order so items with similar indexes are together. For example:
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' ]
It doesn't matter what order the items with similar index go as long as they are together, so a result of 'one','uno','1'...
is a good as what's above. I would like to do it just using immutable variables if possible.
I have a way that works:
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;
})
But it's not very pretty and I don't like it, especially because of the way it mutates the accumulator in the forEach method. I feel there must be a more elegant method.
I can't believe no one has asked this before but I've tried a bunch of different queries and can't find it, so kindly tell me if it's there and I missed it. Is there a better way?
To clarify per question in comments, I would like to be able to do this without mutating any variables or arrays as I'm doing with the accumulator.splice
and to only use functional methods such as .map
, or .reduce
not a mutating loop like a .forEach
.
javascript arrays functional-programming
javascript arrays functional-programming
edited 4 hours ago
jimboweb
asked 4 hours ago
jimbowebjimboweb
1,74111027
1,74111027
"Is there a better way?" What do you mean by "better"? "I don't like it" is not an objective coding problem. What is the requirement?
– guest271314
4 hours ago
Well for one thing I'd like to do it without mutating any variables, or introducing a.forEach
loop which isn't really functional. And I think it can be done much more concisely as well. If I could zip a set of arrays together like in Python then reduce(concat) them together that way would work too.
– jimboweb
4 hours ago
I think I pretty clearly said I'd like the result to not mutate any arrays or variables, as I am doing with theaccumulator.splice
. That is an objective requirement.
– jimboweb
4 hours ago
Does the requirement include a restriction on usingJSON.parse(JSON.stringify(input))
to avoid mutating the original array?
– guest271314
3 hours ago
Wow, lots of great answers, thanks. I'm a little torn on which one to accept as a solution, so I have to try them out. I'll choose a solution in a day or so. but I voted you all up. Thanks again.
– jimboweb
38 mins ago
add a comment |
"Is there a better way?" What do you mean by "better"? "I don't like it" is not an objective coding problem. What is the requirement?
– guest271314
4 hours ago
Well for one thing I'd like to do it without mutating any variables, or introducing a.forEach
loop which isn't really functional. And I think it can be done much more concisely as well. If I could zip a set of arrays together like in Python then reduce(concat) them together that way would work too.
– jimboweb
4 hours ago
I think I pretty clearly said I'd like the result to not mutate any arrays or variables, as I am doing with theaccumulator.splice
. That is an objective requirement.
– jimboweb
4 hours ago
Does the requirement include a restriction on usingJSON.parse(JSON.stringify(input))
to avoid mutating the original array?
– guest271314
3 hours ago
Wow, lots of great answers, thanks. I'm a little torn on which one to accept as a solution, so I have to try them out. I'll choose a solution in a day or so. but I voted you all up. Thanks again.
– jimboweb
38 mins ago
"Is there a better way?" What do you mean by "better"? "I don't like it" is not an objective coding problem. What is the requirement?
– guest271314
4 hours ago
"Is there a better way?" What do you mean by "better"? "I don't like it" is not an objective coding problem. What is the requirement?
– guest271314
4 hours ago
Well for one thing I'd like to do it without mutating any variables, or introducing a
.forEach
loop which isn't really functional. And I think it can be done much more concisely as well. If I could zip a set of arrays together like in Python then reduce(concat) them together that way would work too.– jimboweb
4 hours ago
Well for one thing I'd like to do it without mutating any variables, or introducing a
.forEach
loop which isn't really functional. And I think it can be done much more concisely as well. If I could zip a set of arrays together like in Python then reduce(concat) them together that way would work too.– jimboweb
4 hours ago
I think I pretty clearly said I'd like the result to not mutate any arrays or variables, as I am doing with the
accumulator.splice
. That is an objective requirement.– jimboweb
4 hours ago
I think I pretty clearly said I'd like the result to not mutate any arrays or variables, as I am doing with the
accumulator.splice
. That is an objective requirement.– jimboweb
4 hours ago
Does the requirement include a restriction on using
JSON.parse(JSON.stringify(input))
to avoid mutating the original array?– guest271314
3 hours ago
Does the requirement include a restriction on using
JSON.parse(JSON.stringify(input))
to avoid mutating the original array?– guest271314
3 hours ago
Wow, lots of great answers, thanks. I'm a little torn on which one to accept as a solution, so I have to try them out. I'll choose a solution in a day or so. but I voted you all up. Thanks again.
– jimboweb
38 mins ago
Wow, lots of great answers, thanks. I'm a little torn on which one to accept as a solution, so I have to try them out. I'll choose a solution in a day or so. but I voted you all up. Thanks again.
– jimboweb
38 mins ago
add a comment |
4 Answers
4
active
oldest
votes
Maybe just a simple for... i
loop that checks each array for an item in position 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)
Simple, but predictable and readable
Avoiding For Each Loop
If you need to avoid forEach
, here's a similar approach where you could: get the max child array length, build a range of integers that would've been created by the for loop ([1,2,3,4]
), map each value to pivot the arrays, flatten the multi-dimensional array, and then filter out the empty cells.
First in discrete steps, and then as a one liner:
var input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["1st","2nd","3rd"]];
Multiple Steps:
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);
One Liner:
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)
add a comment |
Use Array.from()
to create a new array with the length of the longest sub array. To get the length of the longest sub array, get an array of the lengths with Array.map()
and take the max item.
Then collect the non undefined items at the current index from each sub array with Array.reduceRight()
or Array.reduce()
(depending on the order you want), and use Array.flat()
to get a single array.
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) => o[i] === undefined ? r : [...r, o[i]], ))
.flat();
console.log(result);
Cool! I like it!
– KyleMit
3 hours ago
-4 bytes[...Array(Math.max(...input.map(o => o.length)))].map((_,i)=>{})
– guest271314
2 hours ago
add a comment |
Funny solution
- add index as prefix on inner array
- Flatten the array
- sort the array
- Remove the prefix
let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]
let ranked=input.map(i=>i.map((j,k)=>k+'---'+j)).slice()
console.log(ranked.flat().sort().map(i=>i.split('---')[1]));
Clever answer. I did consider doing something like this. I was thinking of something more like making the inner map return something like{ind:k,val:j}
followed by a.sort((a,b)=>return a.ind-b.ind)
but it's the same idea. I decided against it because creating unnecessary objects (or strings) felt like doing unnecessary work. But still a good answer, thanks.
– jimboweb
29 mins ago
1
yes this is bit messy , i admit it
– sumit
27 mins ago
add a comment |
Here I have provided a generator function that will yield the values in the desired order. You could easily turn this into a regular function returning an array if you replace the yield
with a push
to a results array to be returned.
The algorithm takes in all the arrays as arguments, then gets the iterators for each of them. Then it enters the main loop where it treats the iters
array like a queue, taking the iterator in front, yielding the next generated value, then placing it back at the end of the queue unless it is empty. The efficiency would improve if you transformed the array into a linked list where adding to the front and back take constant time, whereas a shift
on an array is linear time to shift everything down one spot.
function* collate(...arrays) {
const iters = arrays.map(a => a.values());
while(iters.length > 0) {
const iter = iters.shift();
const {done, value} = iter.next();
if(done) continue;
yield value;
iters.push(iter);
}
}
1
This is a great answer, thanks. But the while loop felt kind of like the .forEach I was trying to get around. But it's still a good answer so I voted it up.
– jimboweb
26 mins ago
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54796607%2freduce-array-of-arrays-into-on-array-in-collated-order%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
Maybe just a simple for... i
loop that checks each array for an item in position 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)
Simple, but predictable and readable
Avoiding For Each Loop
If you need to avoid forEach
, here's a similar approach where you could: get the max child array length, build a range of integers that would've been created by the for loop ([1,2,3,4]
), map each value to pivot the arrays, flatten the multi-dimensional array, and then filter out the empty cells.
First in discrete steps, and then as a one liner:
var input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["1st","2nd","3rd"]];
Multiple Steps:
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);
One Liner:
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)
add a comment |
Maybe just a simple for... i
loop that checks each array for an item in position 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)
Simple, but predictable and readable
Avoiding For Each Loop
If you need to avoid forEach
, here's a similar approach where you could: get the max child array length, build a range of integers that would've been created by the for loop ([1,2,3,4]
), map each value to pivot the arrays, flatten the multi-dimensional array, and then filter out the empty cells.
First in discrete steps, and then as a one liner:
var input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["1st","2nd","3rd"]];
Multiple Steps:
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);
One Liner:
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)
add a comment |
Maybe just a simple for... i
loop that checks each array for an item in position 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)
Simple, but predictable and readable
Avoiding For Each Loop
If you need to avoid forEach
, here's a similar approach where you could: get the max child array length, build a range of integers that would've been created by the for loop ([1,2,3,4]
), map each value to pivot the arrays, flatten the multi-dimensional array, and then filter out the empty cells.
First in discrete steps, and then as a one liner:
var input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["1st","2nd","3rd"]];
Multiple Steps:
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);
One Liner:
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)
Maybe just a simple for... i
loop that checks each array for an item in position 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)
Simple, but predictable and readable
Avoiding For Each Loop
If you need to avoid forEach
, here's a similar approach where you could: get the max child array length, build a range of integers that would've been created by the for loop ([1,2,3,4]
), map each value to pivot the arrays, flatten the multi-dimensional array, and then filter out the empty cells.
First in discrete steps, and then as a one liner:
var input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["1st","2nd","3rd"]];
Multiple Steps:
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);
One Liner:
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)
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)
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)
edited 50 mins ago
answered 4 hours ago
KyleMitKyleMit
58.6k35241401
58.6k35241401
add a comment |
add a comment |
Use Array.from()
to create a new array with the length of the longest sub array. To get the length of the longest sub array, get an array of the lengths with Array.map()
and take the max item.
Then collect the non undefined items at the current index from each sub array with Array.reduceRight()
or Array.reduce()
(depending on the order you want), and use Array.flat()
to get a single array.
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) => o[i] === undefined ? r : [...r, o[i]], ))
.flat();
console.log(result);
Cool! I like it!
– KyleMit
3 hours ago
-4 bytes[...Array(Math.max(...input.map(o => o.length)))].map((_,i)=>{})
– guest271314
2 hours ago
add a comment |
Use Array.from()
to create a new array with the length of the longest sub array. To get the length of the longest sub array, get an array of the lengths with Array.map()
and take the max item.
Then collect the non undefined items at the current index from each sub array with Array.reduceRight()
or Array.reduce()
(depending on the order you want), and use Array.flat()
to get a single array.
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) => o[i] === undefined ? r : [...r, o[i]], ))
.flat();
console.log(result);
Cool! I like it!
– KyleMit
3 hours ago
-4 bytes[...Array(Math.max(...input.map(o => o.length)))].map((_,i)=>{})
– guest271314
2 hours ago
add a comment |
Use Array.from()
to create a new array with the length of the longest sub array. To get the length of the longest sub array, get an array of the lengths with Array.map()
and take the max item.
Then collect the non undefined items at the current index from each sub array with Array.reduceRight()
or Array.reduce()
(depending on the order you want), and use Array.flat()
to get a single array.
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) => o[i] === undefined ? r : [...r, o[i]], ))
.flat();
console.log(result);
Use Array.from()
to create a new array with the length of the longest sub array. To get the length of the longest sub array, get an array of the lengths with Array.map()
and take the max item.
Then collect the non undefined items at the current index from each sub array with Array.reduceRight()
or Array.reduce()
(depending on the order you want), and use Array.flat()
to get a single array.
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) => o[i] === undefined ? r : [...r, o[i]], ))
.flat();
console.log(result);
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) => o[i] === undefined ? r : [...r, o[i]], ))
.flat();
console.log(result);
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) => o[i] === undefined ? r : [...r, o[i]], ))
.flat();
console.log(result);
edited 4 hours ago
answered 4 hours ago
Ori DroriOri Drori
77.9k138492
77.9k138492
Cool! I like it!
– KyleMit
3 hours ago
-4 bytes[...Array(Math.max(...input.map(o => o.length)))].map((_,i)=>{})
– guest271314
2 hours ago
add a comment |
Cool! I like it!
– KyleMit
3 hours ago
-4 bytes[...Array(Math.max(...input.map(o => o.length)))].map((_,i)=>{})
– guest271314
2 hours ago
Cool! I like it!
– KyleMit
3 hours ago
Cool! I like it!
– KyleMit
3 hours ago
-4 bytes
[...Array(Math.max(...input.map(o => o.length)))].map((_,i)=>{})
– guest271314
2 hours ago
-4 bytes
[...Array(Math.max(...input.map(o => o.length)))].map((_,i)=>{})
– guest271314
2 hours ago
add a comment |
Funny solution
- add index as prefix on inner array
- Flatten the array
- sort the array
- Remove the prefix
let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]
let ranked=input.map(i=>i.map((j,k)=>k+'---'+j)).slice()
console.log(ranked.flat().sort().map(i=>i.split('---')[1]));
Clever answer. I did consider doing something like this. I was thinking of something more like making the inner map return something like{ind:k,val:j}
followed by a.sort((a,b)=>return a.ind-b.ind)
but it's the same idea. I decided against it because creating unnecessary objects (or strings) felt like doing unnecessary work. But still a good answer, thanks.
– jimboweb
29 mins ago
1
yes this is bit messy , i admit it
– sumit
27 mins ago
add a comment |
Funny solution
- add index as prefix on inner array
- Flatten the array
- sort the array
- Remove the prefix
let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]
let ranked=input.map(i=>i.map((j,k)=>k+'---'+j)).slice()
console.log(ranked.flat().sort().map(i=>i.split('---')[1]));
Clever answer. I did consider doing something like this. I was thinking of something more like making the inner map return something like{ind:k,val:j}
followed by a.sort((a,b)=>return a.ind-b.ind)
but it's the same idea. I decided against it because creating unnecessary objects (or strings) felt like doing unnecessary work. But still a good answer, thanks.
– jimboweb
29 mins ago
1
yes this is bit messy , i admit it
– sumit
27 mins ago
add a comment |
Funny solution
- add index as prefix on inner array
- Flatten the array
- sort the array
- Remove the prefix
let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]
let ranked=input.map(i=>i.map((j,k)=>k+'---'+j)).slice()
console.log(ranked.flat().sort().map(i=>i.split('---')[1]));
Funny solution
- add index as prefix on inner array
- Flatten the array
- sort the array
- Remove the prefix
let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]
let ranked=input.map(i=>i.map((j,k)=>k+'---'+j)).slice()
console.log(ranked.flat().sort().map(i=>i.split('---')[1]));
let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]
let ranked=input.map(i=>i.map((j,k)=>k+'---'+j)).slice()
console.log(ranked.flat().sort().map(i=>i.split('---')[1]));
let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]
let ranked=input.map(i=>i.map((j,k)=>k+'---'+j)).slice()
console.log(ranked.flat().sort().map(i=>i.split('---')[1]));
edited 2 hours ago
answered 4 hours ago
sumitsumit
8,18993479
8,18993479
Clever answer. I did consider doing something like this. I was thinking of something more like making the inner map return something like{ind:k,val:j}
followed by a.sort((a,b)=>return a.ind-b.ind)
but it's the same idea. I decided against it because creating unnecessary objects (or strings) felt like doing unnecessary work. But still a good answer, thanks.
– jimboweb
29 mins ago
1
yes this is bit messy , i admit it
– sumit
27 mins ago
add a comment |
Clever answer. I did consider doing something like this. I was thinking of something more like making the inner map return something like{ind:k,val:j}
followed by a.sort((a,b)=>return a.ind-b.ind)
but it's the same idea. I decided against it because creating unnecessary objects (or strings) felt like doing unnecessary work. But still a good answer, thanks.
– jimboweb
29 mins ago
1
yes this is bit messy , i admit it
– sumit
27 mins ago
Clever answer. I did consider doing something like this. I was thinking of something more like making the inner map return something like
{ind:k,val:j}
followed by a .sort((a,b)=>return a.ind-b.ind)
but it's the same idea. I decided against it because creating unnecessary objects (or strings) felt like doing unnecessary work. But still a good answer, thanks.– jimboweb
29 mins ago
Clever answer. I did consider doing something like this. I was thinking of something more like making the inner map return something like
{ind:k,val:j}
followed by a .sort((a,b)=>return a.ind-b.ind)
but it's the same idea. I decided against it because creating unnecessary objects (or strings) felt like doing unnecessary work. But still a good answer, thanks.– jimboweb
29 mins ago
1
1
yes this is bit messy , i admit it
– sumit
27 mins ago
yes this is bit messy , i admit it
– sumit
27 mins ago
add a comment |
Here I have provided a generator function that will yield the values in the desired order. You could easily turn this into a regular function returning an array if you replace the yield
with a push
to a results array to be returned.
The algorithm takes in all the arrays as arguments, then gets the iterators for each of them. Then it enters the main loop where it treats the iters
array like a queue, taking the iterator in front, yielding the next generated value, then placing it back at the end of the queue unless it is empty. The efficiency would improve if you transformed the array into a linked list where adding to the front and back take constant time, whereas a shift
on an array is linear time to shift everything down one spot.
function* collate(...arrays) {
const iters = arrays.map(a => a.values());
while(iters.length > 0) {
const iter = iters.shift();
const {done, value} = iter.next();
if(done) continue;
yield value;
iters.push(iter);
}
}
1
This is a great answer, thanks. But the while loop felt kind of like the .forEach I was trying to get around. But it's still a good answer so I voted it up.
– jimboweb
26 mins ago
add a comment |
Here I have provided a generator function that will yield the values in the desired order. You could easily turn this into a regular function returning an array if you replace the yield
with a push
to a results array to be returned.
The algorithm takes in all the arrays as arguments, then gets the iterators for each of them. Then it enters the main loop where it treats the iters
array like a queue, taking the iterator in front, yielding the next generated value, then placing it back at the end of the queue unless it is empty. The efficiency would improve if you transformed the array into a linked list where adding to the front and back take constant time, whereas a shift
on an array is linear time to shift everything down one spot.
function* collate(...arrays) {
const iters = arrays.map(a => a.values());
while(iters.length > 0) {
const iter = iters.shift();
const {done, value} = iter.next();
if(done) continue;
yield value;
iters.push(iter);
}
}
1
This is a great answer, thanks. But the while loop felt kind of like the .forEach I was trying to get around. But it's still a good answer so I voted it up.
– jimboweb
26 mins ago
add a comment |
Here I have provided a generator function that will yield the values in the desired order. You could easily turn this into a regular function returning an array if you replace the yield
with a push
to a results array to be returned.
The algorithm takes in all the arrays as arguments, then gets the iterators for each of them. Then it enters the main loop where it treats the iters
array like a queue, taking the iterator in front, yielding the next generated value, then placing it back at the end of the queue unless it is empty. The efficiency would improve if you transformed the array into a linked list where adding to the front and back take constant time, whereas a shift
on an array is linear time to shift everything down one spot.
function* collate(...arrays) {
const iters = arrays.map(a => a.values());
while(iters.length > 0) {
const iter = iters.shift();
const {done, value} = iter.next();
if(done) continue;
yield value;
iters.push(iter);
}
}
Here I have provided a generator function that will yield the values in the desired order. You could easily turn this into a regular function returning an array if you replace the yield
with a push
to a results array to be returned.
The algorithm takes in all the arrays as arguments, then gets the iterators for each of them. Then it enters the main loop where it treats the iters
array like a queue, taking the iterator in front, yielding the next generated value, then placing it back at the end of the queue unless it is empty. The efficiency would improve if you transformed the array into a linked list where adding to the front and back take constant time, whereas a shift
on an array is linear time to shift everything down one spot.
function* collate(...arrays) {
const iters = arrays.map(a => a.values());
while(iters.length > 0) {
const iter = iters.shift();
const {done, value} = iter.next();
if(done) continue;
yield value;
iters.push(iter);
}
}
answered 2 hours ago
kamoroso94kamoroso94
1,38511316
1,38511316
1
This is a great answer, thanks. But the while loop felt kind of like the .forEach I was trying to get around. But it's still a good answer so I voted it up.
– jimboweb
26 mins ago
add a comment |
1
This is a great answer, thanks. But the while loop felt kind of like the .forEach I was trying to get around. But it's still a good answer so I voted it up.
– jimboweb
26 mins ago
1
1
This is a great answer, thanks. But the while loop felt kind of like the .forEach I was trying to get around. But it's still a good answer so I voted it up.
– jimboweb
26 mins ago
This is a great answer, thanks. But the while loop felt kind of like the .forEach I was trying to get around. But it's still a good answer so I voted it up.
– jimboweb
26 mins ago
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54796607%2freduce-array-of-arrays-into-on-array-in-collated-order%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
"Is there a better way?" What do you mean by "better"? "I don't like it" is not an objective coding problem. What is the requirement?
– guest271314
4 hours ago
Well for one thing I'd like to do it without mutating any variables, or introducing a
.forEach
loop which isn't really functional. And I think it can be done much more concisely as well. If I could zip a set of arrays together like in Python then reduce(concat) them together that way would work too.– jimboweb
4 hours ago
I think I pretty clearly said I'd like the result to not mutate any arrays or variables, as I am doing with the
accumulator.splice
. That is an objective requirement.– jimboweb
4 hours ago
Does the requirement include a restriction on using
JSON.parse(JSON.stringify(input))
to avoid mutating the original array?– guest271314
3 hours ago
Wow, lots of great answers, thanks. I'm a little torn on which one to accept as a solution, so I have to try them out. I'll choose a solution in a day or so. but I voted you all up. Thanks again.
– jimboweb
38 mins ago