Le Duc Anh
Le Duc Anh

Reputation: 141

Convert an array to json object by javascript

I am stuck to solve this problem. Convert an array below

var input = [
    'animal/mammal/dog',
    'animal/mammal/cat/tiger',
    'animal/mammal/cat/lion',
    'animal/mammal/elephant',
    'animal/reptile',
    'plant/sunflower'
]

to json Object

var expectedResult = {
 "animal": {
  "mammal": {
   "dog": true,
   "cat": {
    "tiger": true,
    "lion": true
   },
   "elephant": true
  },
  "reptile": true
 },
 "plant": {
  "sunflower": true
 }
}

Which data structure and algorithm can I apply for it? Thanks

Upvotes: 8

Views: 400

Answers (5)

SaintNo
SaintNo

Reputation: 11

I try with array reduce, hope it help

let input = [
  "animal/mammal/dog",
  "animal/mammal/cat/tiger",
  "animal/mammal/cat/lion",
  "animal/elephant",
  "animal/reptile",
  "plant/sunflower",
];

let convertInput = (i = []) =>
  i.reduce((prev, currItem = "") => {
    let pointer = prev;
    currItem.split("/").reduce((prevPre, currPre, preIdx, arrPre) => {
      if (!pointer[currPre]) {
        pointer[currPre] = preIdx === arrPre.length - 1 ? true : {};
      }
      pointer = pointer[currPre];
    }, {});
    return prev;
  }, {});

console.log(JSON.stringify(convertInput(input), null, 4));

Upvotes: 1

Rajneesh
Rajneesh

Reputation: 5308

Late to the party, here is my try. I'm implmenting recursive approach:

var input = ['animal/mammal/dog', 'animal/mammal/cat/tiger', 'animal/mammal/cat/lion', 'animal/mammal/elephant', 'animal/reptile', 'plant/sunflower'];

result = (buildObj = (array, Obj = {}) => {
  array.forEach((val) => {
    keys = val.split('/');
    (nestedFn = (object) => {
      outKey = keys.shift();
      object[outKey] = object[outKey] || {};
      if (keys.length == 0) object[outKey] = true;
      if (keys.length > 0) nestedFn(object[outKey]);
    })(Obj)
  })
  return Obj;
})(input);

console.log(result);

Upvotes: 1

Cagri Tacyildiz
Cagri Tacyildiz

Reputation: 17570

You need to first split each element to convert to array

using reverse reduce method you can convert them to object.

And your last step is merge this objects.

Lodash.js merge method is an one way to merge them.

var input = ['animal/mammal/dog','animal/mammal/cat/tiger','animal/mammal/cat/lion', 'animal/mammal/elephant','animal/reptile', 'plant/sunflower']
var finalbyLodash={}
input.forEach(x=>{
  const keys = x.split("/");
  const result = keys.reverse().reduce((res, key) => ({[key]: res}), true);
  finalbyLodash = _.merge({}, finalbyLodash, result);
});
console.log(finalbyLodash);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.js"></script>

Upvotes: 4

terrymorse
terrymorse

Reputation: 7086

To make the process more understandable, break the problem down into pieces.

The first step is convert each string into something we can use, converting this:

"animal/mammal/dog"

into this:

[ "animal", "mammal", "dog" ]

That's an array of property names needed to build the final object.

Two functions will accomplish this for you, String.prototype.split() to split the string into an array, and Array.prototype.map() to transform each of the array elements:

let splitIntoNames = input.map(str => str.split('/'));

The intermediate result is this:

[
  [ "animal", "mammal", "dog" ],
  [ "animal", "mammal", "cat", "tiger" ],
  [ "animal", "mammal", "cat", "lion" ],
  [ "animal", "mammal", "elephant" ],
  [ "animal", "reptile" ],
  [ "plant", "sunflower" ]
]

Next step is to iterate over each array, using Array.prototype.forEach() to add properties to the object. You could add properties to the object with a for loop, but let's do that with a recursive function addName():

function addName(element, list, index) {
  if (index >= list.length) {
    return;
  }
  let name = list[index];
  let isEndOfList = index === list.length - 1;

  element[name] = element[name] || (isEndOfList ? true : {});

  addName(element[name], list, index + 1);
}

let result = {};
splitIntoNames.forEach((list) => {
  addName(result, list, 0);
});

The result:

result: {
  "animal": {
    "mammal": {
      "dog": true,
      "cat": {
        "tiger": true,
        "lion": true
      },
      "elephant": true
    },
    "reptile": true
  },
  "plant": {
    "sunflower": true
  }
}

const input = [
  "animal/mammal/dog",
  "animal/mammal/cat/tiger",
  "animal/mammal/cat/lion",
  "animal/mammal/elephant",
  "animal/reptile",
  "plant/sunflower",
];

let splitIntoNames = input.map((str) => str.split("/"));
console.log("splitIntoNames:", JSON.stringify(splitIntoNames, null, 2));

function addName(element, list, index) {
  if (index >= list.length) {
    return;
  }
  let name = list[index];
  let isEndOfList = index === list.length - 1;

  element[name] = element[name] || (isEndOfList ? true : {});

  addName(element[name], list, index + 1);
}

let result = {};
splitIntoNames.forEach((list) => {
  addName(result, list, 0);
});
console.log("result:", JSON.stringify(result, null, 2));

Upvotes: 3

Rian Tavares
Rian Tavares

Reputation: 147

You can create a function that will slice every element from the array by "/" than you put the results into a variable and than just mount the Json. I mean something like that below:


    window.onload = function() {
      var expectedResult;
      var input = [
        'animal/mammal/dog',
        'animal/mammal/cat/tiger',
        'animal/mammal/cat/lion',
        'animal/mammal/elephant',
        'animal/reptile',
        'plant/sunflower'
    ]

      input.forEach(element => {
        var data = element.split('/');

        var dog = data[2] === 'dog' ? true : false
        var tiger = data[2] === 'cat' && data[3] === 'tiger'  ? true : false
        var lion = data[2] === 'cat' && data[3] === 'lion'  ? true : false


        expectedResult = {
          data[0]: {
            data[1]: {
             "dog": dog,
             "cat": {
              "tiger": tiger,
              "lion": lion
             }
            }
          }
        }
      })
    }

Upvotes: 1

Related Questions