Search code examples
javascriptobjectecmascript-6lodashflatten

Flatten nested array of objects, renaming keys to an iterator


I have an array of objects, with each object looking like the following (a poll response):

{
"slug": "18-AZ-Gov-GE-DvF",
"name": "2018 Arizona Gubernatorial GE",
"tags": [],
"charts": [],
"election_date": "2018-11-06",
"n_polls": 1,
"created_at": "2017-06-13T13:32:26.000Z",
"responses": [
 {
 "label": "Ducey",
 "name": "Doug Ducey",
 "party": "Republican",
 "incumbent": true
 },
 {
 "label": "Farley",
 "name": "Steve Farley",
 "party": "Democrat",
 "incumbent": false
 },
 {
 "label": "Other",
 "name": "Other",
 "party": null,
 "incumbent": false
 },
 {
 "label": "Undecided",
 "name": "Undecided",
 "party": null,
  "incumbent": false
 }
]
},

I need to flatten the array accessed by the responses key so that each object keyed to its iterator.

The final object should look like:

{
"slug": "18-AZ-Gov-GE-DvF",
"name": "2018 Arizona Gubernatorial GE",
"tags": [],
"charts": [],
"election_date": "2018-11-06",
"n_polls": 1,
"created_at": "2017-06-13T13:32:26.000Z",
 "label1": "Ducey",
 "name1": "Doug Ducey",
 "party1": "Republican",
 "incumbent1": true
 "label2": "Farley",
 "name2": "Steve Farley",
 "party2": "Democrat",
 "incumbent2": false
 "label3": "Other",
 "name3": "Other",
 "party3": null,
 "incumbent3": false
 "label4": "Undecided",
 "name4": "Undecided",
 "party4": null,
  "incumbent4": false
},

The answers I've seen do not rename the object keys when flattening or are performed on a collection.

I've tried several solutions, but wanted to see if there's an easy es6 way before I really dive in.


Solution

  • One approach would use a few of the newer goodies including spread, destructuring assignment, template literals, entries and most importantly reduce.

    The main gist is using a reducer to convert the responses array to a new object with each element object using a secondary reducer to amend the key with the iterator count and assigning the value to the outer object with the new key.

    A key benefit of this approach is the original object (including its sub-objects) are not modified (read: no side-effects).

    const flattened = responses.reduce((o, g, i) => {
        Object.entries(g).reduce((t, [k, v]) => {
          t[`${k}${i + 1}`] = v;
          return t;
        }, o);
    
        return o;
      },
      {});
    

    Full working example:

    const orig = {
      "slug": "18-AZ-Gov-GE-DvF",
      "name": "2018 Arizona Gubernatorial GE",
      "tags": [],
      "charts": [],
      "election_date": "2018-11-06",
      "n_polls": 1,
      "created_at": "2017-06-13T13:32:26.000Z",
      "responses": [{
          "label": "Ducey",
          "name": "Doug Ducey",
          "party": "Republican",
          "incumbent": true
        },
        {
          "label": "Farley",
          "name": "Steve Farley",
          "party": "Democrat",
          "incumbent": false
        },
        {
          "label": "Other",
          "name": "Other",
          "party": null,
          "incumbent": false
        },
        {
          "label": "Undecided",
          "name": "Undecided",
          "party": null,
          "incumbent": false
        }
      ]
    };
    
    const {responses, ...foo} = orig;
    const flattened = responses.reduce((o, g, i) => {
        Object.entries(g).reduce((t, [k, v]) => {
          t[`${k}${i + 1}`] = v;
          return t;
        }, o);
    
        return o;
      },
      {});
    
    console.log({flattened: {...foo, ...flattened}});
    console.log({orig});