Home > Enterprise >  How to combine single array elements based on similar property in JavaScript?
How to combine single array elements based on similar property in JavaScript?

Time:04-07

I am looking for this logic if possible in ES6 to combine 2 array elements in single array.

Below is data coming from backend.

dataFromBackend = [
    {
      animal: 'Elephant',
      height: 123,
      weight: 456
    },
    {
      animal: 'Elephant',
      food: 'abc',
      sleep: 'xyz'
    },
    {
      animal: 'Tiger',
      height: 123,
      weight: 456
    },
    {
      animal: 'Tiger',
      food: 'abc',
      sleep: 'xyz'
    },
    {
      animal: 'Rabbit',
      height: 123,
      weight: 456
    }
]

Expected Output

convertedData = [
    {
      animal: 'Elephant',
      height: 123,
      weight: 456,
      food: 'abc',
      sleep: 'xyz'
    },
    {
      animal: 'Tiger',
      height: 123,
      weight: 456,
      food: 'abc',
      sleep: 'xyz'
    },
    {
      animal: 'Rabbit',
      height: 123,
      weight: 456,
      food: 'No Data',
      sleep: 'No Data'
    }
]

And if there is no data for certain property I have to insert 'No Data'

CodePudding user response:

Here you have an example of how to process the dataFromBackend array and how to produce the result array with elements as the merge of objects having the same value for animal property:

//your input array (just with compact formatting for easier readying)
dataFromBackend = [
    { animal: 'Elephant', height: 123, weight: 456 },
    { animal: 'Elephant', food: 'abc', sleep: 'xyz' },
    { animal: 'Tiger', height: 123, weight: 456 },
    { animal: 'Tiger', food: 'abc', sleep: 'xyz' },
    { animal: 'Rabbit', height: 123, weight: 456 }
]

//the map will be a temporary object
var map = {};

//for each element in the input array
dataFromBackend.forEach( (o, i) => {
  //if there's yet no animal in map like o.animal
  if(typeof map[o.animal] === 'undefined')
    //init the animal inside the map
    map[o.animal] = o;
  //else if the animal already existed in the map
  else      
    //integrate the properties of the object stored in map
    map[o.animal] = {...map[o.animal], ...o};
});

//print the map to console
console.log(map);

//the result array
var result = [];
//for each key in map
Object.keys(map).forEach((k) => {
    //push the merged animal in the result array
    result.push( map[k] );
});

//print the result array
console.log(result);

CodePudding user response:

I would use the reduce method to group the elements by the animal property at first, and then go through them a second time to assign the missing properties with 'No Data':

dataFromBackend = [
    { animal: 'Elephant', height: 123, weight: 456 },
    { animal: 'Elephant', food: 'abc', sleep: 'xyz' },
    { animal: 'Tiger', height: 123, weight: 456 },
    { animal: 'Tiger', food: 'abc', sleep: 'xyz' },
    { animal: 'Rabbit', height: 123, weight: 456 }
];

let rawResult = dataFromBackend .reduce((acc, element) => {
  if (!acc[element.animal]) {
    acc[element.animal] = element
  }
  acc[element.animal] = { ...acc[element.animal], ...element }
  return acc;
}, {});

let convertedData = Object.values(rawResult).map(r => ({
  ...r,
  food: r.food ?? 'No Data',
  sleep: r.sleep ?? 'No Data'
}));

console.log(convertedData);

CodePudding user response:

I would create a function by which you can merge the obj's by giving the data and prop.

  dataFromBackend = [
    { animal: 'Elephant', height: 123, weight: 456 },
    { animal: 'Elephant', food: 'abc', sleep: 'xyz' },
    { animal: 'Tiger', height: 123, weight: 456 },
    { animal: 'Tiger', food: 'abc', sleep: 'xyz' },
    { animal: 'Rabbit', height: 123, weight: 456 },
  ];
  
    // prop by merge by, arr of objects
 function mergeByProp(prop, arr) {
    let returnArr = [];
    for (let obj of arr) {
      if (returnArr.some((target) => target[prop] === obj[prop])) {
        let target = returnArr.find((find) => find[prop] === obj[prop]);
        Object.assign(target, { ...target, ...obj });
      } else {
        returnArr.push(obj);
      }
    }
    console.log(returnArr);
  }
  
this.mergeByProp('animal', this.dataFromBackend);

CodePudding user response:

I would construct a dictionary and group all animals by name, and then merge them with the spread operator

type Animal = {
  animal: string;
  height?: number | string;
  weight?: number | string;
  food?: string;
  sleep?: string;
};

@Component({
  selector: 'app-one',
  templateUrl: './one.component.html',
  styleUrls: ['./one.component.scss'],
})
export class OneComponent {
  dataFromBackend: Animal[] = [
    { animal: 'Elephant', height: 123, weight: 456 },
    { animal: 'Elephant', food: 'abc', sleep: 'xyz' },
    { animal: 'Tiger', height: 123, weight: 456 },
    { animal: 'Tiger', food: 'abc', sleep: 'xyz' },
    { animal: 'Rabbit', height: 123, weight: 456 },
  ];

  convertedData: Animal[] = [];

  ngOnInit() {
    const animalDict: { [key: string]: Animal[] } = {};
    for (const el of this.dataFromBackend) {
      if (!animalDict[el.animal]) animalDict[el.animal] = [];
      animalDict[el.animal].push(el);
    }
    for (const animalName of Object.keys(animalDict)) {
      let animal: Animal = {
        animal: animalName,
        height: 'No Data',
        weight: 'No Data',
        food: 'No Data',
        sleep: 'No Data',
      };
      for (const entry of animalDict[animalName]) {
        animal = { ...animal, ...entry };
      }
      this.convertedData.push(animal);
    }
  }
}

Stackblitz: https://stackblitz.com/edit/angular-ivy-chvdcw?file=src/app/app.component.ts

  • Related