1

Suppose this is my input: "0.1412898495873448 -0.03307049805768848 -0.0002348551519150674 0.0007142371877833145 -0.01250041632738383 0.4052674201000387 -0.02541421100956797 -0.02842612870208528 1803.701163338969 1796.443677744862 1986.31441429052 1442.354524622483"

Sylvester has this constructor. It requires a nested array.

It's simple enough to make the string into an array of numbers using String.split() and map and friends.

Something not unlike this:

var nums = line.split(/ |\n/);
nums.map(function(num) {return parseFloat(num);});

But I think Javascript is missing a functional-style function that can "group" my 16-array into 4 rows of 4 numbers.

I thought maybe a JS utility library such as underscore might have me covered.

Doesn't look like it.

What's the most elegant and/or efficient way to do this? Do I have to use a for loop?

function nest(array, range) {
    var l = array.length;
    var ret = [];
    var cur = null;
    for (var i=0; i<l; ++i) {
        if (!(i%range)) {
            cur = [];
            ret.push(cur);
        }
        cur.push(array[i]);
    }
    return ret;
}

Node tells me this works:

> function nest(array, range) {
...     var l = array.length;
...     var ret = [];
...     var cur = null;
...     for (var i=0; i<l; ++i) {
.....         if (!(i%range)) {
.......             cur = [];
.......             ret.push(cur);
.......         }
.....         cur.push(array[i]);
.....     }
...     return ret;
... }
undefined
> nest([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],4)
[ [ 0, 1, 2, 3 ],
  [ 4, 5, 6, 7 ],
  [ 8, 9, 10, 11 ],
  [ 12, 13, 14, 15 ] ]
>

Can anyone think of a better way to do it?

Steven Lu
  • 41,389
  • 58
  • 210
  • 364

2 Answers2

2

There are several ways to split an array into sub-arrays

var line = "0.1412898495873448 -0.03307049805768848 -0.0002348551519150674 0.0007142371877833145 -0.01250041632738383 0.4052674201000387 -0.02541421100956797 -0.02842612870208528 1803.701163338969 1796.443677744862 1986.31441429052 1442.354524622483";

var nums = line.split(/ |\n/).map(parseFloat);
var columns = 4;
var nested = [];
for (var i=0; i < nums.length; i+=columns) {
  nested.push(nums.slice(i, i+columns));
}

console.log(nested);

Or functional way:

var nested = Array.apply(null, new Array((nums.length/columns)|0))
               .map(function(item, index) {
                  return nums.slice(index*columns, (index+1) * columns);
               });

Using splice instead of slice seems to be much better, though it might be due to badly written tests

var columns = 4;
var nested = [];
for (var i=nums.length/columns; i--;) { // gives expected result if columns===0
  nested.push(nums.splice(0,columns));
}

Alternatively:

var columns = 4;
var nested = [];
while(nums.length) nested.push(nums.splice(0,columns));
Tibos
  • 27,507
  • 4
  • 50
  • 64
  • Slice is probably the ticket. Better than invoking push over and over. – Steven Lu Jan 24 '14 at 23:36
  • look at the performance test in my answer, i was so interested, i think my one is much faster because i avoid .map dont know – john Smith Jan 24 '14 at 23:50
  • @johnSmith I am accepting the first code listing, the second one clearly can't be performant. (Your code listing is more or less the one I initially came up with) – Steven Lu Jan 25 '14 at 00:49
  • BTW: I would have accepted an answer that consisted of only the word `slice`. Also, the case of `columns` being 0 should be checked. It would be an infinite loop. – Steven Lu Jan 25 '14 at 00:51
  • @StevenLu haha yeah my code is the other way arround of your second one, didnt notice that, eventhough fast isnt always elegant. and in this code and my code a 0 wont cause an infinite loop i think, for my one im even sure – john Smith Jan 25 '14 at 01:00
0

Here is my approach,

var nums = line.split(/ |\n/);
var temp=[],output=[],i=0;
while(i<nums.length){
    temp.push(i);
    if(i%4 == 0){ 
        output.push(temp);
        temp=[];
    }
    i++
}
console.dir(output);

For my senses this is prettty fast! check this performance test against the accepted answer http://jsperf.com/comparing-use-of-modulo-vs-slice-in-a-special-case

john Smith
  • 17,409
  • 11
  • 76
  • 117
  • Your jsPerf is pretty biased, not to mention wrong. Some obvious issues are that you added the parseFloat to my solution which is missing from yours. It doesn't matter much, because you don't actually lookup the numbers/strings in your solution either, you just add the index in the output arrays. I fixed it here and the slice solution is as expected faster both on this specific case and on bigger arrays where performance actually matters: http://jsperf.com/comparing-use-of-modulo-vs-slice-in-a-special-case/2 – Tibos Jan 25 '14 at 11:15
  • Also, i added a splice case which for some reason is much faster than the slice version. My guess is that there's some crazy V8 optimization going on there, but still, if you want to go for performance, that's probably the solution to use. – Tibos Jan 25 '14 at 11:23
  • yeah, it was like, i wanted to comment " im too lazy to test if thats fast" so i i ended up with my first js performance test-sheet :D, but in your new test sheet, you included the map function in preparation code wich is not included in my attempt and not needed, so thats the reason, if i remove it my one is faster again ;) – john Smith Jan 25 '14 at 11:49
  • nah forget my comment, the preparation code wont be included to duration – john Smith Jan 25 '14 at 14:31