Home > Blockchain >  How to flatten nested array of objects and create new array
How to flatten nested array of objects and create new array

Time:01-26

I have a data structure as the following

[
   {
      "models":[
         {
            "name":"xyz",
            "options":[
               {
                  "label":"blue"
               },
               {
                  "label":"brown"
               },
            ]
         },
         {
            "name":"abc",
            "options":[
               {
                  "label":"yellow"
               }
            ]
         },
         {
            "name":"def",
            "options":[
               {
                  "label":"green"
               }
            ]
         }
      ]
   }
]

The end result should be an array with all of the labels and name like xyz: blue, xyz: brown, abc: yellow, def: green

so something like this

['xyz: blue', 'xyz: brown', 'abc: yellow','def: green']

I'm trying different approaches, one with RxJS operators and another with reduce

let flat = (acc, current) => {

}
models.reduce(flat, [])

CodePudding user response:

You can use a reduce and a map like this.

const arr = [
   {
      "models":[
         {
            "name":"xyz",
            "options":[
               {
                  "label":"blue"
               },
               {
                  "label":"brown"
               },
            ]
         },
         {
            "name":"abc",
            "options":[
               {
                  "label":"yellow"
               }
            ]
         },
         {
            "name":"def",
            "options":[
               {
                  "label":"green"
               }
            ]
         }
      ]
   }
];

const result = arr[0].models.reduce(
  (acc, model) => [...acc, ...model.options.map(i => ({ [model.name]: i.label }))]
  , []
);

console.log(result);

If the top level array can have multiple items rather than arr[0] you would need to add another reduce feeding it's accumulator in to the second reduce as it's starting accumulator rather than the empty starting array.

const arr = [
   {
      "models":[
         {
            "name":"xyz",
            "options":[
               {
                  "label":"blue"
               },
               {
                  "label":"brown"
               },
            ]
         },
         {
            "name":"abc",
            "options":[
               {
                  "label":"yellow"
               }
            ]
         },
         {
            "name":"def",
            "options":[
               {
                  "label":"green"
               }
            ]
         }
      ]
   },
   {
      "models":[
         {
            "name":"ghi",
            "options":[
               {
                  "label":"gold"
               },
               {
                  "label":"pink"
               },
            ]
         }
      ]
   }
];

const result = arr.reduce(
  (acc, item) =>
    item.models.reduce(
      (acc2, model) => [...acc2, ...model.options.map((i) => ({ [model.name]: i.label }))]
      , acc
    ),
  []
);


console.log(result);

Not sure where RxJs comes into this question but if you are looking to transform an object like this that comes back from a http request you would pipe it into the map operator and then use this function inside the map. If you are looking to do a reduce on a stream there is a reduce operator that emits the accumulator when the source stream completes or the scan operator that emits the accumulator each time the source emits.

CodePudding user response:

Use nested calls to flatMap(), and in the innermost call you concatenate the model name with the option label.

const data = [{
  "models": [{
      "name": "xyz",
      "options": [{
          "label": "blue"
        },
        {
          "label": "brown"
        },
      ]
    },
    {
      "name": "abc",
      "options": [{
        "label": "yellow"
      }]
    },
    {
      "name": "def",
      "options": [{
        "label": "green"
      }]
    }
  ]
}];

let result = data.flatMap(d => d.models.flatMap(model => model.options.map(option => `${model.name}: ${option.label}`)));

console.log(result);

CodePudding user response:

Here is using multiple forEach and destructuring

const flat = (arr, res = []) => {
  arr.forEach(({ models }) =>
    models.forEach(({ name, options }) =>
      options.forEach(({ label }) => res.push({ [name]: label }))
    )
  );
  return res;
};

const data = [
  {
    models: [
      {
        name: "xyz",
        options: [
          {
            label: "blue",
          },
          {
            label: "brown",
          },
        ],
      },
      {
        name: "abc",
        options: [
          {
            label: "yellow",
          },
        ],
      },
      {
        name: "def",
        options: [
          {
            label: "green",
          },
        ],
      },
    ],
  },
];

console.log(flat(data));

CodePudding user response:

const response = array[0].models.reduce((initial, model) => {
  if (model.options.length === 1)
    initial.push(`${model.name}: ${model.options[0].label}`);
  else {
    model.options.map((option) =>
      initial.push(`${model.name}: ${option.label}`),
    );
  }
  return initial;
}, []);
console.log(response)

; // there is no need if the inner option has just one object you can just access it by model.options[0].label, that why there is a check to see if it one

  • Related