简体   繁体   中英

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.

Using the array .reduce method might work well here. Since the reduce callback will receive the current index as the third argument, you can use that to create the keys you're looking for.

Example:

 const myArray = [ { "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 flattened = myArray.reduce((flat, item, index) => ({ ...flat, ...Object.keys(item).reduce((numbered, key) => ({ ...numbered, [key + (index+1)]: item[key], }), {}), }), {}); console.log(flattened); 

You can use forEach to go through the responses array and assign a new key on your object for the entries in each object. After that just delete the original responses array:

 let obj = {"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 }]} obj.responses.forEach((item, i) => { // item is one object from responses // i is the index starting at 0. Concat that on the key Object.entries(item).forEach(([k, v]) => obj[k+(i+1)] = v) }) // no need for obj.responses any more delete obj.responses console.log(obj) 

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

You can destructure your responses and other data, then reduce each response to one object with indexed keys and then assemble result by object spread operator:

const { responses, ...other } = data
const indexedResponses = responses.reduce((acc, r, i) => {
  Object.entries(r).forEach(([key, value]) => {
    acc[`${key}${i + 1}`] = value
  })
  return acc
}, {})
const result = { ...other, ...indexedResponses }

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