Map and reduce array of objects with a children ar

2019-08-22 11:59发布

问题:

I'm trying to take an array of objects like this

[
  {
    "id": "uniqueParentId1",
    "children": [
      {
        "childProp1": "test1",
        "childProp2": "test3"
      }
    ]
  },
  {
    "id": "uniqueParentId2",
    "children": [
      {
        "childProp1": "somevals",
        "childProp2": "other vals"
      },
      {
        "childProp1": "somevals 1",
        "childProp2": "other vals 1"
      }
    ]
  }
]

And return an array of all the children combined, with each child object having an additional value, the "id" of the parent.

Above examples, result.

[
  {
    "parentId": "uniqueParentId1",
    "childProp1": "test1",
    "childProp2": "test3"
  },
  {
    "parentId": "uniqueParentId2",
    "childProp1": "somevals",
    "childProp2": "other vals"
  }
  {
    "parentId": "uniqueParentId2",
    "childProp1": "somevals 1",
    "childProp2": "other vals 1"
  }
]

I'm just not sure how to approach this. I'm familiar with flattening and array of arrays. But I'm only able to get the output as an array of the original children without adding the parentId

回答1:

This should do it:

var values = [{
    "id": "uniqueParentId1",
    "children": [{
      "childProp1": "test1",
      "childProp2": "test3"
    }]
  },
  {
    "id": "uniqueParentId2",
    "children": [{
        "childProp1": "somevals",
        "childProp2": "other vals"
      },
      {
        "childProp1": "somevals 1",
        "childProp2": "other vals 1"
      }
    ]
  }
];

var result = values.map(value =>
  value.children.map(child => Object.assign({ parentId: value.id }, child))
).reduce((l, n) => l.concat(n), []);

console.log(result);

A breakdown of the code:

child => Object.assign({ parentId: value.id }, child)

Takes an object and returns a copy of that object with a parentId property added. value.id uses the closure variable value, which is outside the function.

Input:

{
    "childProp1": "somevals",
    "childProp2": "other vals"
}

Output:

{
    "parentId": "uniqueParentId2"
    "childProp1": "somevals",
    "childProp2": "other vals"
}

Next function:

value =>
    value.children.map(child => Object.assign({ parentId: value.id }, child))

Takes an object named value, applies the function above to each of the array elements in value.children, and returns an array of the results.

Next:

values.map(.....)

Applies the function above to each of the elements in values and returns an array of the results.

At this point, the result of this .map() call is an array like the following, with one element for each element of the original array:

[
  [
    {
      "parentId": "uniqueParentId1",
      "childProp1": "test1",
      "childProp2": "test3"
    }
  ],
  [
    {
      "parentId": "uniqueParentId2",
      "childProp1": "somevals",
      "childProp2": "other vals"
    },
    {
      "parentId": "uniqueParentId2",
      "childProp1": "somevals 1",
      "childProp2": "other vals 1"
    }
  ]
]

So the last thing we do is flatten flatten this array with .reduce((l, r) => l.concat(r), []), which is a generic way to flatten an array. This takes an empty array as a "seed" value, and one by one concatenates arrays together to produce the final result.

Example of this flattening in action:

var arrayOfArrays = [[1,2,3],[4,5],[6,7,8,9]];

var explain = (l, r) => 
    console.log('concatenating', JSON.stringify(l), 'with', JSON.stringify(r));

var result = arrayOfArrays.reduce(
    (l, r) => (explain(l, r), l.concat(r)), []);

console.log("result:", JSON.stringify(result));



回答2:

const arr = [
  {
    "id": "uniqueParentId1",
    "children": [
      {
        "childProp1": "test1",
        "childProp2": "test3"
      }
    ]
  },
  {
    "id": "uniqueParentId2",
    "children": [
      {
        "childProp1": "somevals",
        "childProp2": "other vals"
      },
      {
        "childProp1": "somevals 1",
        "childProp2": "other vals 1"
      }
    ]
  }
];
let combined = [];
arr.forEach((parent) => {
  const {id, children} = parent;
  children.forEach((child) => {
    Object.assign(child, {
      parentId: id
    });
    combined.push(child);
  });
});

console.log(combined);
/*
[
  {
    "parentId": "uniqueParentId1",
    "childProp1": "test1",
    "childProp2": "test3"
  },
  {
    "parentId": "uniqueParentId2",
    "childProp1": "somevals",
    "childProp2": "other vals"
  }
  {
    "parentId": "uniqueParentId2",
    "childProp1": "somevals 1",
    "childProp2": "other vals 1"
  }
]
*/


回答3:

If you apply a more functional approach, you can do something like:

const results = [] //Array we want to store all data in
arr.forEach(e => {
  const {id: parentId} = e  //Extract id as variable parentId

  // Store all the children objects with the added key
  e.children.forEach(c => results.push(Object.assign({parentId}, c)))
})