简体   繁体   中英

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.

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); 

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)

Plain javascript version:

var newArr = [];
var j=0;
var k=0;
for (var i = 0; i <data.length; i++) {
    if(data[i].type == 'parent'){
        newArr[j] = data[i];
        newArr[j].children = [];
        j++;
        k=0;
    } 
    else {
        data[i].name = newArr[j-1].name.toLowerCase() + String.fromCharCode(k + 97)
        newArr[j-1].children[k] =data[i];
        k++;
    }
}
console.log(newArr)

I am assuming here that the parent is always placed before the children, as provided in your example data.

Also, it would be good if you can prevent parents with 26+ children. This would cause String.fromCharCode(k + 97) to print strange characters. For this, see http://www.asciitable.com/

for (ele in data)
{
    if (!data[ele].hasOwnProperty('child') && data[ele].type=='parent')
    {
        data[ele].child = [];
        while(data[parseInt(ele) + 1] && data[parseInt(ele) + 1].type == 'child')
        {
            data[ele].child.push({type: data[parseInt(ele) + 1].type, name:data[parseInt(ele) + 1].name});
            data.splice(parseInt(ele) + 1, 1);
        }
    }
}
console.log(data);

Try simple loop:

var current, parent, result = [], i = 0;

while(current = data[i++]){

    if(current.type === "parent"){
        current.child = [];
        result.push(current);
        parent = current
    }else{
        current.name = (parent.name + String.fromCharCode(parent.child.length + 97)).toLowerCase();
        parent.child.push(current)
    }

}

Demo

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM