skip to Main Content

I have a flat array that i nest together based on childrens parentFolderId. I need to remove items where none of the children, or children’s children below it contains the property in_shop

My example data looks like this:

const menuItems = [
  {
    id: 1,
    displayName: "Equipment",
    parentFolderId: null,
    // ... rest of object
  },
    {
    id: 2,
    displayName: "Equipment",
    parentFolderId: 6,
    // ... rest of object
  },
    {
    id: 3,
    displayName: "Equipment",
    parentFolderId: 2,
    in_shop: true
    // ... rest of object
  }, 
    {
    id: 4,
    displayName: "Equipment",
    parentFolderId: 1,
    // ... rest of object
  },
    {
    id: 5,
    displayName: "Equipment",
    parentFolderId: 1,
    // ... rest of object
  },
    {
    id: 6,
    displayName: "Equipment",
    parentFolderId: null,
    // ... rest of object
  },
    {
    id: 7,
    displayName: "Equipment",
    parentFolderId: 5,
    in_shop: true,
    // ... rest of object
  },
    {
    id: 8,
    displayName: "Equipment",
    parentFolderId: 3,
    // ... rest of object
  },
  // ... rest of objects
];

I want a result that looks like this, based on the input above:

const menuItems = [
  {
    id: 1,
    displayName: "Equipment",
    parentFolderId: null,
    // ... rest of object
    children: [
      {
        id: 5,
        displayName: "Equipment",
        parentFolderId: 1,
        children: [
          {
            id: 7,
            displayName: "Equipment",
            parentFolderId: 5,
            in_shop: true,
            // ... rest of object
          },
        ]
        // ... rest of object
      },
    ],
  },

  {
    id: 6,
    displayName: "Equipment",
    parentFolderId: null,
    // ... rest of object
    children: [
      {
        id: 2,
        displayName: "Equipment",
        parentFolderId: 6,
        // ... rest of object
        children: [
          {
            id: 3,
            displayName: "Equipment",
            parentFolderId: 2,
            in_shop: true,
            // ... rest of object
          },
        ],
      },
    ],
  },
  // ... rest of objects
]

I cant wrap my head around how to recursively loop from the bottom of the array, then back up.

My code that sorts array under children looks like this:

function addChild(obj) {
    // get children and further retrieve its children with map recursively
    let children = menuItems.filter(a => a.parentFolderId == obj.id).map(addChild)

    // if children are found then add childs in object ALso check if item is in shop
    if (children.length > 0 ) {
      return { ...obj, children }
    }

    // if no children found then return object only
    return { ...obj }
  }
const result = menuItems.filter(a => a.parentFolderId == null).map(addChild)

3

Answers


  1. Updating the return value as follows:

    return { ...obj, children, in_shop: obj.in_shop || children.some(child => child.in_shop) }
    
    Login or Signup to reply.
  2. For each an in_shop item walk through parents (with caching in an object). Since the order of in_shop items seems random – sort the result by id:

    const map = {};
    const result = [];
    
    const walk = item => 
      !item.parentFolderId && result.push(item) || // add root items to the result
      map[item.parentFolderId]?.children.push(item) || // if cached, just add to children
      walk(map[item.parentFolderId] = {...menuItems.find(({id}) => id === item.parentFolderId), children: [item]});
      
    const sort = items => items?.sort((a, b) => a.id - b.id).forEach(({children}) => sort(children));
    
    menuItems.forEach(item => item.in_shop && walk({...item}));
    sort(result);
    
    console.log('original array is preserved:', JSON.stringify(menuItems) === originalJSON);
    
    console.log(result);
    <script>
    const menuItems = [
      {
        id: 1,
        displayName: "Equipment",
        parentFolderId: null,
        // ... rest of object
      },
        {
        id: 2,
        displayName: "Equipment",
        parentFolderId: 6,
        // ... rest of object
      },
        {
        id: 3,
        displayName: "Equipment",
        parentFolderId: 2,
        in_shop: true
        // ... rest of object
      }, 
        {
        id: 4,
        displayName: "Equipment",
        parentFolderId: 1,
        // ... rest of object
      },
        {
        id: 5,
        displayName: "Equipment",
        parentFolderId: 1,
        // ... rest of object
      },
        {
        id: 6,
        displayName: "Equipment",
        parentFolderId: null,
        // ... rest of object
      },
        {
        id: 7,
        displayName: "Equipment",
        parentFolderId: 5,
        in_shop: true,
        // ... rest of object
      },
        {
        id: 8,
        displayName: "Equipment",
        parentFolderId: 3,
        // ... rest of object
      },
      // ... rest of objects
    ];
    const originalJSON = JSON.stringify(menuItems);
    </script>
    ` Chrome/119
    ------------------------------------------------------------
    Alexander        1.00x  |  x1000000  316  325  336  367  397
    Nina Sholz       3.35x  |   x100000  106  110  111  116  125
    Peter Seliger   32.28x  |    x10000  102  103  109  110  111
    ------------------------------------------------------------
    https://github.com/silentmantra/benchmark `
    
    const menuItems = [{
        id: 1,
        displayName: "Equipment",
        parentFolderId: null,
        // ... rest of object
      }, {
        id: 2,
        displayName: "Equipment",
        parentFolderId: 6,
        // ... rest of object
      }, {
        id: 3,
        displayName: "Equipment",
        parentFolderId: 2,
        in_shop: true
        // ... rest of object
      }, {
        id: 4,
        displayName: "Equipment",
        parentFolderId: 1,
        // ... rest of object
      }, {
        id: 5,
        displayName: "Equipment",
        parentFolderId: 1,
        // ... rest of object
      }, {
        id: 6,
        displayName: "Equipment",
        parentFolderId: null,
        // ... rest of object
      }, {
        id: 7,
        displayName: "Equipment",
        parentFolderId: 5,
        in_shop: true,
        // ... rest of object
      }, {
        id: 8,
        displayName: "Equipment",
        parentFolderId: 3,
        // ... rest of object
      }];
      
    //@benchmark Nina Sholz
    const temp = function (data, root) {
            var t = {};
            data.forEach(o => {
                Object.assign(t[o.id] = t[o.id] || {}, o);
                ((t[o.parentFolderId] ??= {}).children ??= []).push(t[o.id]);
            });
            return t[root].children;
        }(menuItems, null),
        shop = (r, { children = [], ...o}) => {
            children = children.reduce(shop, []);
            if (!children.length) children = undefined;
            if (o.in_shop || children) r.push({ ...o, children });
            return r;
        };
    temp.reduce(shop, []);
      
    //@benchmark Alexander
    const map = {};
    const result = [];
    
    const walk = item => 
      !item.parentFolderId && result.push(item) || // add root items to the result
      map[item.parentFolderId]?.children.push(item) || // if cached, just add to children
      walk(map[item.parentFolderId] = {...menuItems.find(({id}) => id === item.parentFolderId), children: [item]});
      
    const sort = items => items?.sort((a, b) => a.id - b.id).forEach(({children}) => sort(children));
    
    menuItems.forEach(item => item.in_shop && walk({...item}));
    sort(result);
    result;
    
    /*@end*/eval(atob('e2xldCBlPWRvY3VtZW50LmJvZHkucXVlcnlTZWxlY3Rvcigic2NyaXB0Iik7aWYoIWUubWF0Y2hlcygiW2JlbmNobWFya10iKSl7bGV0IHQ9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic2NyaXB0Iik7dC5zcmM9Imh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9naC9zaWxlbnRtYW50cmEvYmVuY2htYXJrL2xvYWRlci5qcyIsdC5kZWZlcj0hMCxkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKHQpfX0='));
    Login or Signup to reply.
  3. You could reduce the tree by looking to children and property in_shop.

    const
        menuItems = [{ id: 1, displayName: "Equipment", parentFolderId: null }, { id: 2, displayName: "Equipment", parentFolderId: 6 }, { id: 3, displayName: "Equipment", parentFolderId: 2, in_shop: true }, { id: 4, displayName: "Equipment", parentFolderId: 1 }, { id: 5, displayName: "Equipment", parentFolderId: 1 }, { id: 6, displayName: "Equipment", parentFolderId: null }, { id: 7, displayName: "Equipment", parentFolderId: 5, in_shop: true }, { id: 8, displayName: "Equipment", parentFolderId: 3 }],
        temp = function (data, root) {
            var t = {};
            data.forEach(o => {
                Object.assign(t[o.id] = t[o.id] || {}, o);
                ((t[o.parentFolderId] ??= {}).children ??= []).push(t[o.id]);
            });
            return t[root].children;
        }(menuItems, null),
        shop = (r, { children = [], ...o}) => {
            children = children.reduce(shop, []);
            const sub = children.length
                ? { children }
                : {};
            if (o.in_shop || sub.children) r.push({ ...o, ...sub });
            return r;
        };
        tree = temp.reduce(shop, []);
    
    console.log(temp);
    console.log(tree);
    .as-console-wrapper { max-height: 100% !important; top: 0; }
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search