control order of array when flattening nested array

Viewed 39

I have an array of objects called dogs, each object has a key called type with a string value and a key called images with an array of objects. I want to flatten the array and copy the type key to each object in images array. I've managed to achieve this which produces the following result:

[
  {
    "type": "labrador",
    "src": "unique-url"
  },
  {
    "type": "labrador",
    "src": "unique-url"
  },
  {
    "type": "labrador",
    "src": "unique-url"
  },
  {
    "type": "german-shepherd",
    "src": "unique-url"
  },
  {
    "type": "german-shepherd",
    "src": "unique-url"
  },
  {
    "type": "german-shepherd",
    "src": "unique-url"
  },
  {
    "type": "husky",
    "src": "unique-url"
  },
  {
    "type": "husky",
    "src": "unique-url"
  },
  {
    "type": "husky",
    "src": "unique-url"
  }
]

However, I want to maintain the order of the initial array so it looks something like:

[
  {
    "type": "labrador",
    "src": "unique-url"
  },
  {
    "type": "german-shepherd",
    "src": "unique-url"
  },
  {
    "type": "husky",
    "src": "unique-url"
  },
  {
    "type": "labrador",
    "src": "unique-url"
  },
  {
    "type": "german-shepherd",
    "src": "unique-url"
  },
  {
    "type": "husky",
    "src": "unique-url"
  },
  {
    "type": "labrador",
    "src": "unique-url"
  },
  {
    "type": "german-shepherd",
    "src": "unique-url"
  },
  {
    "type": "husky",
    "src": "unique-url"
  }
]

How do I go about this? Any pointers would be greatly appreciated

const dogs = [
  {
    type: 'labrador',
    images: [
      { src: 'unique-url'},
      { src: 'unique-url'},
      { src: 'unique-url'}
    ]
  },
  {
    type: 'german-shepherd',
    images: [
      { src: 'unique-url'},
      { src: 'unique-url'},
      { src: 'unique-url'}
    ]
  },
  {
    type: 'husky',
    images: [
      { src: 'unique-url'},
      { src: 'unique-url'},
      { src: 'unique-url'}
    ]
  },
]

const result = dogs
    .map((d) =>
      d.images.map((i) => ({
        type: d.type,
        ...i,
      }))
    )
    .flat()

console.log(result)

1 Answers

A couple of for loops might be a simple way to go. Here the outer loop proceeds through the indexes of the images arrays whilst the inner loop takes each dog in turn:

const dogs = [
  {
    type: 'labrador',
    images: [
      { src: 'unique-url-l1'},
      { src: 'unique-url-l2'},
      { src: 'unique-url-l3'}
    ]
  },
  {
    type: 'german-shepherd',
    images: [
      { src: 'unique-url-g1'},
      { src: 'unique-url-g2'},
      { src: 'unique-url-g3'}
    ]
  },
  {
    type: 'husky',
    images: [
      { src: 'unique-url-h1'},
      { src: 'unique-url-h2'},
      { src: 'unique-url-h3'}
    ]
  },
];

const inner_length = Math.max(...dogs.map((dog) => dog.images?.length || 0));

let arr = [];
for(let i = 0; i < inner_length; i++) {
  for(let j = 0; j < dogs.length; j++) {
    let image = dogs[j].images?.[i];
    
    if(image) {
      arr.push({ type: dogs[j].type, ...image });
    }
  }
}

console.log(arr);

A reduce method more similar to the question code:

const dogs = [
  {
    type: 'labrador',
    images: [
      { src: 'unique-url-l1'},
      { src: 'unique-url-l2'},
      { src: 'unique-url-l3'}
    ]
  },
  {
    type: 'german-shepherd',
    images: [
      { src: 'unique-url-g1'},
      { src: 'unique-url-g2'},
      { src: 'unique-url-g3'},
    ]
  },
  {
    type: 'husky',
    images: [
      { src: 'unique-url-h1'},
      { src: 'unique-url-h2'},
      { src: 'unique-url-h3'}
    ]
  },
];

let arr = dogs
  .reduce((acc, dog) =>
    dog.images.reduce((acc2, image, i) => {
      acc2[i] ??= [];
      acc2[i].push({ type: dog.type, ...image });
      return acc2;
    }, acc)
  , [])
  .flat();

console.log(arr);

The above uses what I think is called a bucket sort to group the images by their original (inner) array positions prior to flattening out the entire array.

Related