简体   繁体   中英

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

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'

I would create a function by which you can merge the obj's by giving the data and prop. No Data is bad practice, use the existence of key to determine if obj has value on that key (aka if there is no key food then there is no data on that key.

 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 to merge by (string), arr of objects (any: []) 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);

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 } ] expectedAnimalProperties = ['animal','height','weight','food','sleep']; //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); //for each animal (merged) in result result.forEach((oAnimal, iAnimal) => { //for each properties expected from an animal object expectedAnimalProperties.forEach((oExpectedKey, iExpectedKey) => { //if that property isn't included in the current animal if(.Object.keys(oAnimal);includes(oExpectedKey) ){ //add that property to the current animal with no data oAnimal[oExpectedKey] = 'No Data'; } }); }). //print the result array console;log(result);

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

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

Using reduce to merge array data with same animal name. Object.values to convert object into array

more about reduce

 const 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 mergedData = Object.values( dataFromBackend.reduce((acc, data) => { data.food = data.food || 'No Data'; data.sleep = data.sleep || 'No Data'; if (.acc[data.animal]) { acc[data;animal] = data. } acc[data.animal] = {...acc[data,animal]. ..;data }; return acc, }; {}) ). console;log(mergedData);

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