Home > OS >  Use rxjs to group JSON data in Angular app
Use rxjs to group JSON data in Angular app

Time:10-04

I need to have my data in a form like this to be able to do 2 nested ngFor loops:

this.groupedCities = [
  {
    label: 'Germany', value: 'de', 
      items: [
        {label: 'Berlin', value: 'Berlin'},
        {label: 'Frankfurt', value: 'Frankfurt'},
        {label: 'Hamburg', value: 'Hamburg'},
        {label: 'Munich', value: 'Munich'}
      ]
  },
  {
    label: 'USA', value: 'us', 
      items: [
        {label: 'Chicago', value: 'Chicago'},
        {label: 'Los Angeles', value: 'Los Angeles'},
        {label: 'New York', value: 'New York'},
        {label: 'San Francisco', value: 'San Francisco'}
        ]
  },
];

But my back end only supplies it like this:

this.data = [         
  {label: 'Berlin', value: 'Berlin', country: 'Germany'},
  {label: 'Frankfurt', value: 'Frankfurt', country: 'Germany'},
  {label: 'Hamburg', value: 'Hamburg', country: 'Germany'},
  {label: 'Munich', value: 'Munich', country: 'Germany'}
  {label: 'Chicago', value: 'Chicago', country: 'USA'},
  {label: 'Los Angeles', value: 'Los Angeles', country: 'USA'},
  {label: 'New York', value: 'New York', country: 'USA'},
  {label: 'San Francisco', value: 'San Francisco', country: 'USA'}
];

I seem to remember a way to do this in rxjs but I can't remember it.

CodePudding user response:

You could achieve this by a combination of these RxJS operators

  1. groupBy
  2. mergeMap
  3. reduce
  4. toArray
import { from } from 'rxjs'; 
import { groupBy, mergeMap, reduce, toArray } from 'rxjs/operators';
const mapper = {
  'Germany': 'de',
  'USA': 'us'
};
const data$ = from([
  {label: 'Berlin', value: 'Berlin', country: 'Germany'},
  {label: 'Frankfurt', value: 'Frankfurt', country: 'Germany'},
  {label: 'Hamburg', value: 'Hamburg', country: 'Germany'},
  {label: 'Munich', value: 'Munich', country: 'Germany'},
  {label: 'Chicago', value: 'Chicago', country: 'USA'},
  {label: 'Los Angeles', value: 'Los Angeles', country: 'USA'},
  {label: 'New York', value: 'New York', country: 'USA'},
  {label: 'San Francisco', value: 'San Francisco', country: 'USA'}
]);
const grouped$ = data$.pipe(
  groupBy(city => city.country),
  mergeMap(group => group
    .pipe(
      reduce((acc, cur) => {
          const item = {label: cur.label, value: cur.value};
          acc.items.push(item);
          return acc;
        },
        { key: group.key, value: mapper[group.key], items: [] }
      )
    )
  ),
  toArray()
);

grouped$.subscribe(console.log);

Demo: https://playcode.io/976662

CodePudding user response:

Hope this helps; I notice there is no key for abbr for the country code

const partition = (arr, fn) =>
    arr.reduce(
        (acc, val, i, arr) => {
            acc[fn(val, i, arr) ? 0 : 1].push(val);
            return acc;
        },
        [[], []]
    );

let data = [
    {label: 'Berlin', value: 'Berlin', country: 'Germany'},
    {label: 'Frankfurt', value: 'Frankfurt', country: 'Germany'},
    {label: 'Hamburg', value: 'Hamburg', country: 'Germany'},
    {label: 'Munich', value: 'Munich', country: 'Germany'},
    {label: 'Chicago', value: 'Chicago', country: 'USA'},
    {label: 'Los Angeles', value: 'Los Angeles', country: 'USA'},
    {label: 'New York', value: 'New York', country: 'USA'},
    {label: 'San Francisco', value: 'San Francisco', country: 'USA'}
];

const [germany, nonGermany] = partition(data, o=> o.country === 'Germany').map((arr) =>{
    return arr.reduce((acc, cv, i) => {
        if(i === 0){
            acc = {...acc, label: cv.country, value: '', items: [{label: cv.label, value: cv.value}]}
            return acc
        }
        return {
            ...acc,
            items:[
                ...acc.items,
                {label: cv.label, value: cv.value}
            ]
        };
    }, {})
});

console.log(germany);
console.log(nonGermany);
  • Related