1

I have an object

1:a
2:b
3:c
4:d
5:e
6:f
7:g
8:h
9:i

I want to make a 3D array, like

[
  [ [a], [b], [c] ],
  [ [d], [e], [f] ],
  [ [g], [h], [i] ]
] 

Is it possible to write a for-loop to push first three letters to an 3D array, than second three letters and third at the end (like in example)?

Andreas
  • 21,535
  • 7
  • 47
  • 56
Natali
  • 65
  • 1
  • 11
  • _Is it possible to write a for-loop to push..._ Try to see if possible or not, agree? BTW your example of the 3D array doesn't make any sense...you dont need the 3rd dimension as your example is, because your 2nd dimension contains only 1 element at each index – B001ᛦ Jun 22 '17 at 15:15
  • It is possible. Did you try anything using a loop? – JstnPwll Jun 22 '17 at 15:16
  • Show us your efforts to solve this. – kk. Jun 22 '17 at 15:26

4 Answers4

2

As per @Andreas comment Does JavaScript Guarantee Object Property Order?.

properties order in objects is not guaranteed. So, if you need to rely on keyVal order a new step must be done: sorting on Object.keys.

A solution can be based looping on Object.keys and modulus operator in order to group by col or division for a row ordering:

var obj = {1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e', 6: 'f', 7: 'g', 8: 'h', 9: 'i'};

var resultByCol = [];
var resultByRow = [];
var objKeys = Object.keys(obj).sort(function(a, b) {
    return a - b;
});
for (var idx = 0; idx < objKeys.length; idx++) {
    var newIdx = idx % 3;
    if (resultByCol[newIdx] === undefined) {
        resultByCol.push([]);
    }
    resultByCol[newIdx].push([obj[objKeys[idx]]]);



    newIdx = Math.floor(idx / 3);
    if (resultByRow[newIdx] === undefined) {
        resultByRow.push([]);
    }
    resultByRow[newIdx].push([obj[objKeys[idx]]]);
}


console.log('Grouped by Col: [');
for (var i = 0; i < resultByCol.length; i++) {
    console.log('\t' + JSON.stringify(resultByCol[i]) + (((i + 1) == resultByCol.length) ? '' : ','));
}
console.log(']');

console.log('Grouped by Row: [');
for (i = 0; i < resultByRow.length; i++) {
    console.log('\t' + JSON.stringify(resultByRow[i]) + (((i + 1) == resultByRow.length) ? '' : ','));
}
console.log(']');
gaetanoM
  • 41,594
  • 6
  • 42
  • 61
  • Staying away from `forEach` would make this function much faster. In fact in many enterprise coding environments using `forEach` is actually prohibited and will not pass peer review due to it being so much slower than a simple `for` loop. – StephenRios Jun 22 '17 at 15:22
  • @StephenRios Thanks so much for sharing. – gaetanoM Jun 22 '17 at 15:24
  • [Does JavaScript Guarantee Object Property Order?](https://stackoverflow.com/questions/5525795/does-javascript-guarantee-object-property-order) – Andreas Jun 22 '17 at 15:25
2

You can first sort array of Object.keys() and then use reduce() to create new array by each 3 keys.

var obj = {1: 'a',2: 'b',3: 'c',4: 'd',5: 'e',6: 'f',7: 'g',8: 'h',9: 'i',}

var result = Object.keys(obj).sort((a, b) => a - b).reduce(function(r, e, i) {
  if(i % 3 == 0) r.push([]);
  r[r.length - 1].push([obj[e]])
  return r;
}, [])

console.log(result)
Nenad Vracar
  • 118,580
  • 15
  • 151
  • 176
  • You don't need `c` and `n`. The third parameter of the `.reduce()` callback is the index of the current element -> `if (index % 3 === 0) r.push([]);`. And `c` can be replaced with `r.length - 1` -> `r[r.length - 1].push([ obj[e] ]);`: https://jsfiddle.net/vnsdwj51/ – Andreas Jun 22 '17 at 15:40
1

This can be achieved very quickly and efficiently using keys and a simple for loop to keep your code expedient.

@gaetanoM's solution will work just fine, but for a more performant function you can do the following:

var object = {1:'a', 2:'b', 3:'c', 4:'d', 5:'e', 6:'f', 7:'g', 8:'h', 9:'i'};
var keys = Object.keys(object);
var result = [];
for ( var i=, j=keys.length; i<j; i++){
    result[i] = object[keys[i]];
}
StephenRios
  • 2,192
  • 4
  • 26
  • 42
1

Yes. There are a number of ways to do it. Probably the cleanest looking would be to use the reduce() function:

const obj = { 1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e', 6: 'f', 7: 'g', 8: 'h', 9: 'i' };

const width = 3; // how many in each sub array.
const result = Object.keys(obj).map(key => obj[key]) // ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
  .reduce((result, v) => {
    if (result[result.length - 1].length === width) {
      result.push([v]); // add new row
    } else {
      result[result.length - 1].push(v); // add to last row
    }
    return result;
  }, [[]]);
  
console.log(result);

This is a little more verbose then it needs to be, but more understandable.

First, I loop through and map() the object to a normal array.

Then, in the reduce() function, I start it with the first level of nested arrays. Then on each loop, I check the length of the last array in result. If it's full, I add a new array for the next row. Otherwise, I just add the value to the last row.

A more compact version would be:

const obj = { 1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e', 6: 'f', 7: 'g', 8: 'h', 9: 'i' };
const width = 3;

const result = Object.keys(obj).reduce((r, k) => 
  r.concat(
    r[r.length - 1].length === width 
      ? [[obj[k]]]
      : [r.pop().concat(obj[k])]
  ), [[]]);

console.log(result);

This example makes more use of .concat() so everything is technically on one line, though obviously it's a good bit harder to read and understand.

samanime
  • 25,408
  • 15
  • 90
  • 139