1

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, [])
0

4 Answers 4

3

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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

7 Comments

Interesting, I'm getting an error saying flatMap doesn't exist
What environment/version are you using? See this compatibility table
the time complexity of this code will increase because the exist 3 loop inside the code. since you are sure that the array has an object, you can index it by array[0].models. this has no loop in it and it better that array.flatMap. @Barmar
I'm not sure that the top-level array only has one object. I assumed that was just an example, and it could be more.
well it was stated in the data as you can see that the data only has one object @Barmar
|
1

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

Comments

-1
const response = array[0].models.reduce((initial, model) => {
  if (model.options.length === 1)
    initial.push(`${model.name}: ${model.options[0].label}`);
  else {
    model.options.forEach((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

1 Comment

That is a terrible use of map, might as well use forEach rather than map in that situation. initial is the worst name for an accumulator I think you could probably use. No point for the if statement as the map iteration covers it in the case of length == 1 anyway. Everything about this answer smells bad. I would absolutely reject this in a pull request.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.