Home > Mobile >  flatten a nested array of objects using RxJS
flatten a nested array of objects using RxJS

Time:04-13

our data structure is a nested array with objects

interface dtModel {
  id: number;
  permission: number;
  childs: dtModel[];
}

const data: dtModel[] = [
  {
    id: 1,
    permission: 2,
    childs: [
      {
        id: 2,
        permission: 1,
        childs: [
          {
            id: 3,
            permission: 3,
            childs: [],
          },
        ],
      },
    ],
  },
  {
    id: 4,
    permission: 1,
    childs: [
      {
        id: 5,
        permission: 2,
        childs: [
          {
            id: 6,
            permission: 3,
            childs: [
              {
                id: 7,
                permission: 1,
                childs: [],
              },
            ],
          },
        ],
      },
    ],
  },
];

I tried with help of RxJs to create a flattened array like this which is I'm not succeeded yet

[
  {id:1,permission:2},
  {id:2,permission:1},
  {id:3,permission:3},
  {id:4,permission:1},
  {id:5,permission:2},
  {id:6,permission:3},
  {id:7,permission:1}
]

I tried to do that with help of 'reduce' but because my childs' property does not always have the same length I couldn't do that with this approach.

of(data)
  .pipe(
    map((items: dtModel[]) => {
      return items.reduce((res, curr) => {
        res.push({ id: curr.id, permission: curr.permission });
        return res;
      }, []);
    })
  )
  .subscribe(console.log);

StackBlitz

CodePudding user response:

need to make recursion

const source = [
  {
    id: 1,
    permission: 2,
    childs: [
      {
        id: 2,
        permission: 1,
        childs: [
          {
            id: 3,
            permission: 3,
            childs: [],
          },
        ],
      },
    ],
  },
  {
    id: 4,
    permission: 1,
    childs: [
      {
        id: 5,
        permission: 2,
        childs: [
          {
            id: 6,
            permission: 3,
            childs: [
              {
                id: 7,
                permission: 1,
                childs: [],
              },
            ],
          },
        ],
      },
    ],
  },
];

const result  = [];

const getAllItemsPerChildren = (item) => {
  result.push({ id: item.id, permission: item.permission });
  if (item.childs) {
    return item.childs.map((i) => getAllItemsPerChildren(i));
  }
};

source.forEach((i) => getAllItemsPerChildren(i));

console.log(result)

RXJS SOLUTION

demo: https://stackblitz.com/edit/typescript-dlva1z?file=index.ts


const source$ = of({
  data: [],
  last: false,
  check: source,
}).pipe(
  expand((data) =>
    data.last
      ? EMPTY
      : of(data.check).pipe(
          map((currentItem) => {
            const childs = (currentItem as any).flatMap((i) => i?.childs || []);
            return {
              data: currentItem.map((i) => ({
                id: i.id,
                permissions: i.permission,
              })),
              last: !childs?.length,
              check: childs,
            };
          })
        )
  ),
  reduce((acc, items) => [...acc, ...(items as any).data], [])
);
source$.subscribe((data) => console.log('result ', data));

Explanation:

  • The main idea to use expand operator - it allow us to create recursion.
  • To stop recursion need to return EMPTY
  • of({data: [],last: false, check: source}) - it is our store
  • data is assigned to keep parsed item - that will be used in reduce operator - to concat the whole data
  • one return from expand equal to one emit
  • last allows to control recusrion, we are checking if all items is parsed
  • check - element to parse, intial value set to your entire data

CodePudding user response:

You need to do a tree (= your data structure) traversal and for every node you have to push {id://some id,permission://some permission} on a result array.

You don't need rxjs for that.

CodePudding user response:

A simplified expand version, just need to ensure every item is emitted and do some clean up at the end with map

from(data)
  .pipe(
    expand((obj) => {
      return obj.childs && obj.childs.length
        ? from(obj.childs)
        : EMPTY;
    }),
    map(obj=>({ id: obj.id, permission: obj.permission })),
    toArray()
  )
  .subscribe(console.log);
  • Related