Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to split javascript array of object with specific condition using lodash/underscorejs

I have array of objects like this:

var data = [
 {
    type : "parent",
    name : "A"
 },
 {
    type : "child",
    name : "1"
 },
 {
    type : "child",
    name : "2"
 },
 {
    type : "parent",
    name : "B"
 },
 {
    type : "child",
    name : "3"
 }
]

and I want to move child objects into parent objects, splitted by the parrent object (there is no given key from child object is belonged to which parrent). So it's only separate by the parent object. To be simple I want to change the array into :

[
  {
    type : "parent",
    name : "A",
    child: [
        {
            type : "child",
            name : "1"
        },
        {
            type : "child",
            name : "2"
        }
    ]
  },
  {
    type : "parent",
    name : "B",
    child: [
        {
            type : "child",
            name : "3"
        }
      ]
  }
]

I have read lodash about chunk but it's no use.

like image 977
waskito Avatar asked Jun 04 '15 13:06

waskito


2 Answers

You can use either the native Array.prototype.reduce function or lodash's reduce:

var data = [{
    type: "parent",
    name: "A"
  },
  {
    type: "child",
    name: "1"
  },
  {
    type: "child",
    name: "2"
  },
  {
    type: "parent",
    name: "B"
  },
  {
    type: "child",
    name: "3"
  }
];

// If using _.reduce then use:
// var newData = _.reduce(data, function(arr, el) {...}, []);
var newData = data.reduce(function(arr, el) {
  if (el.type === 'parent') {
    // If el is pushed directly it would be a reference
    // from the original data object
    arr.push({
      type: el.type,
      name: el.name,
      child: []
    });
  } else {
    arr[arr.length - 1].child.push({
      type: el.type,
      name: el.name
    });
  }

  return arr;
}, []);

console.log(newData);

UPDATE: Small changes using newer ES language features

const data = [{
    type: "parent",
    name: "A"
  },
  {
    type: "child",
    name: "1"
  },
  {
    type: "child",
    name: "2"
  },
  {
    type: "parent",
    name: "B"
  },
  {
    type: "child",
    name: "3"
  }
];

const newData = data.reduce((arr, el) => {
  if (el.type === 'parent') {
    // If el is pushed directly it would be a reference
    // from the original data object
    arr.push({...el, child: []});
  } else {
    arr[arr.length - 1].child.push({...el});
  }

  return arr;
}, []);

console.log(newData);
like image 156
Jason Cust Avatar answered Oct 14 '22 01:10

Jason Cust


Here's a lodash solution that may be a bit easier to understand. CodePen

A few notes:

  • this modifies the incoming data object - if that's a problem we can toss in some _.clone() calls.
  • This will only work if each parent has 26 or fewer children, because of the name: "ab" pattern you picked
var lastParent;
var result = _.chain(data)
  .groupBy(function (item) {
    if (item.type === 'parent') lastParent = item.name
    return lastParent
  })
  .map(function (group) {
    var parent = _.first(group)
    parent.child = _.chain(group)
      .slice(1)
      .map(function (child, index) {
        child.name = parent.name.toLowerCase() + String.fromCharCode(index + 97)
        return child 
      })
      .value()
    return parent
  })
  .value()

console.log(result)
like image 39
Brandon Horst Avatar answered Oct 14 '22 02:10

Brandon Horst