1

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

3 Answers 3

2

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
Sign up to request clarification or add additional context in comments.

5 Comments

I know about recursion, and I want to try to reach this point with RxJS operators.
give me a few mins
I have added rxjs solution
it it helps you can mark my anser as correct)
shortest ever ( based on stackoverflow.com/a/71857389/9200941): from(source).pipe(expand((i) => (i?.childs?.length ? from(i.childs) : EMPTY)), reduce((a, i) => [...a, { id: i.id, permission: i.permission }], [])).subscribe(console.log);
2

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

working example: https://codesandbox.io/s/rxjs-playground-forked-vr0l6s?file=/src/index.js

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

very good article explains expand usage https://ncjamieson.com/understanding-expand/

Comments

0

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.

2 Comments

do you mean something like this: let final = []; const flatArr = (arr: any[]) => { arr.forEach((item) => { final.push({ id: item.id, permission: item.permission }); if (item.childs.length > 0) flatArr(item.childs); }); };
The answer above does exactly what you want: stackoverflow.com/a/71856452/2976617

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.