user10553736
user10553736

Reputation:

Javascript - convert nested arrays into object of arrays

I have a nested array with unknown shape. Here is an example:

["head","val1","val2","val3",
    ["head2","val4","val5",
        ["head3","val6","val7", 
            ["head4", "val8"],"val9"]],
    ["head5", "val10", "val11"]
]

The arrays all have a length of 2 or greater. An array can contain any number of other arrays which can also contain any number of arrays. All values are either strings or arrays.

I'm trying to convert this into a single object with the following shape:

{"head": ["val1","val2","val3", 
    {"head2": ["val4","val5", 
        {"head3": ["val6","val7", 
            {"head4": ["val8"]}, "val9"]},
    {"head5": ["val10", "val11"]}
]}

Basically, each array needs to be converted to an object where the first value is the key and the rest of the array is the value. I've tried using reduce but can't quite get it right.

Upvotes: 1

Views: 283

Answers (6)

Akrion
Akrion

Reputation: 18515

Since you tagged this with lodash here is a short solution with it as well:

var data = ["head","val1","val2","val3", ["head2","val4","val5", ["head3","val6","val7", ["head4", "val8"],"val9"]], ["head5", "val10", "val11"] ]

const f = (d) => ({[_.head(d)]: _.map(_.tail(d), x => _.isArray(x) ? f(x) : x)})

console.log(f(data))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

It is a recursive solution which utilizes lodash .head to get the first element of the array, .tail to get all but the first and then _.map to go over each of the elements and return an array.

Upvotes: 0

cybersam
cybersam

Reputation: 66989

This efficient approach avoids using slice() (which would create a temporary array of size N-1 that gets dropped):

function change(a) {
  let b = [];
  for (let i = 1; i < a.length; i++) {
    let v = a[i];
    b.push(Array.isArray(v) ? change(v) : v);
  }
  return {[a[0]]: b};
}

console.log(change(
  ["head","val1","val2","val3",
    ["head2","val4","val5",
        ["head3","val6","val7", 
            ["head4", "val8"],"val9"]],
    ["head5", "val10", "val11"]
  ]
));

Upvotes: 0

ibrahim mahrir
ibrahim mahrir

Reputation: 31682

Just do it recursively like so:

function convert(arr) {
  return {
    [arr[0]]: arr.slice(1).map(item => Array.isArray(item)? convert(item): item)
  }
}

The function convert returns an object containg one key-value pair. The key is arr[0] and the value is the rest of the elements in the array (arr.slice(1)) mapped into a new array such that convert is called on all array elements inside that array.

ES5 Version:

function convert(arr) {
  var obj = {};
  obj[arr[0]] = arr.slice(1).map(function(item) {
      return item instanceof Array? convert(item): item;
  });
  return obj;
}

Example:

function convert(arr) {
  return {
    [arr[0]]: arr.slice(1).map(item => Array.isArray(item)? convert(item): item)
  }
}

let arr = ["head","val1","val2","val3",
    ["head2","val4","val5",
        ["head3","val6","val7", 
            ["head4", "val8"],"val9"]],
    ["head5", "val10", "val11"]
];

let result = convert(arr);

console.log(result);

Upvotes: 2

Sylwester
Sylwester

Reputation: 48745

Something like this?

function convert(arr) {
  if (arr instanceof Array) {
    const [key, ...values] = arr;
    return { [key]: values.map(convert) };
  } else {
    return arr;
  }
}

const test = ["head","val1","val2","val3",
    ["head2","val4","val5",
        ["head3","val6","val7", 
            ["head4", "val8"],"val9"]],
    ["head5", "val10", "val11"]
];

console.log(convert(test));

Upvotes: 1

Joshua K
Joshua K

Reputation: 2537

ES6: You can do something like the following

function convertArrayToObject(arr) {
  return Array.isArray(arr) && arr.length ? {
    [arr[0]]: arr.slice(1).map(convertArrayToObject)
  } : arr;
}

This Code defines a function convertArrayToObject which returns the element itself if it's not an array or sets the first element as the key and calls the function if with the remaining elements as the values if it's an array.

Upvotes: 0

dana
dana

Reputation: 18105

In this case, you don't know how deep your nested arrays are, so it is a good idea to design a recursive function.

In the base case of your recursive function, the input is not an array, so just return the input.

In the case when the input is an array, create a JS object with a single property whose name is equal to the first element in the input array, and whose value is equal to the rest of the array... You need to recursively call your function against each of the remaining members to get their value in the resulting array.

var a = ["head","val1","val2","val3",
    ["head2","val4","val5",
        ["head3","val6","val7", 
            ["head4", "val8"],"val9"]],
    ["head5", "val10", "val11"]
];

function convert(val) {
  var result;
  if (Array.isArray(val)) {
    var key = val[0];
    var vals = val.slice(1);
    result = {};
    result[key] = [];
    for (var i = 0; i < vals.length; i++) {
      result[key].push(convert(vals[i]));
    }
  } else {
    result = val;
  }
  return result;
}

console.log(convert(a));

Upvotes: 3

Related Questions