简体   繁体   中英

Filter deeply nested data and return all parent / immediate child items JS

I have a data question that I am finding difficulty in how to search for (or even title this question). I don't get the chance to have a great deal of exposure to data-related tasks with JS and I would like to learn the best approaches. If you would please explain the approach you've used in the answer, I would greatly appreciate it.

We have a RESTful service that returns a very generic structure of data, which allows for it to be different depending on the use case. There are filters (dropdowns) that the user may select from to return a subset of results from the entire data set. When a user selects a filter from, lets say the middle of the data structure, it should return all associated parent properties, as well as the immediate children of the items property (for the next level of filtering to choose from).

Currently, what I've done is just create a helper function that iterates over the data passed through, being the current level of the data. If the value exists at that level, push it to a list (and just keep iterating through by calling the method again until there are no more children). The problem is that this creates a flat structure, and I lose all association with its parents, and it returns all children in each object. Here is a CodePen from that. I feel like this approach is leading down a rabbit hole if I try to finagle the parents and filter the children somehow.

const data = {
  level : 'Level 1',
  items : [
    {
      name  : 'Some Business Name',
      id    : '123',
      data  : null,
      child : {
        level : 'Level 2',
        items : [
          {
            name  : 'Some Sub-Business Name',
            id    : '1234',
            data  : null,
            child : {
              level : 'Level 3',
              items : [
                {
                  name  : 'Some Area Name',
                  id    : '12345',
                  data  : null,
                  child : {
                    level : 'Level 4',
                    items : [
                      {
                        name  : 'Some Local Name',
                        id    : '123456',
                        data  : null,
                        child : {
                          level : 'Level 5',
                          items : [
                            {
                              name  : 'Some Product Name',
                              id    : '1234567',
                              data  : [2, 35, 235, 35554, 55554],
                              child : null
                            },
                            {
                              name  : 'Some Product Name 2',
                              id    : '12345678',
                              data  : [9, 5, 35, 5764, 335],
                              child : null
                            }
                          ]
                        }
                      },
                      {
                        name  : 'Some Local Name 2',
                        id    : '123456',
                        data  : null,
                        child : {
                          level : 'Level 5',
                          items : [
                            {
                              name  : 'Some Product Name 3',
                              id    : '1234567',
                              data  : [2, 35, 235, 35554, 55554],
                              child : null
                            },
                            {
                              name  : 'Some Product Name 4',
                              id    : '12345678',
                              data  : [9, 5, 35, 5764, 335],
                              child : null
                            }
                          ]
                        }
                      }
                    ]
                  }
                },
                {
                  name  : 'Some Area Name 2',
                  id    : '12345',
                  data  : null,
                  child : {
                    level : 'Level 4',
                    items : [
                      {
                        name  : 'Some Local Name 3',
                        id    : '123456',
                        data  : null,
                        child : {
                          level : 'Level 5',
                          items : [
                            {
                              name  : 'Some Product Name 5',
                              id    : '1234567',
                              data  : [2, 35, 235, 35554, 55554],
                              child : null
                            }
                          ]
                        }
                      }
                    ]
                  }
                }
              ]
            }
          }
        ]
      }
    },
    {
      name  : 'Some Business Name 2',
      id    : '123',
      data  : null,
      child : {
        level : 'Level 2',
        items : [
          {
            name  : 'Some Sub-Business Name 2',
            id    : '1234',
            data  : null,
            child : {
              level : 'Level 3',
              items : [
                {
                  name  : 'Some Area Name 3',
                  id    : '12345',
                  data  : null,
                  child : {
                    level : 'Level 4',
                    items : [
                      {
                        name  : 'Some Local Name 4',
                        id    : '123456',
                        data  : null,
                        child : {
                          level : 'Level 5',
                          items : [
                            {
                              name  : 'Some Product Name 6',
                              id    : '1234567',
                              data  : [2, 35, 235, 35554, 55554],
                              child : null
                            },
                            {
                              name  : 'Some Product Name 7',
                              id    : '12345678',
                              data  : [9, 5, 35, 5764, 335],
                              child : null
                            }
                          ]
                        }
                      }
                    ]
                  }
                }
              ]
            }
          },
          {
            name  : 'Some Sub-Business Name 3',
            id    : '1234',
            data  : null,
            child : {
              level : 'Level 3',
              items : [
                {
                  name  : 'Some Area Name 5',
                  id    : '12345',
                  data  : null,
                  child : {
                    level : 'Level 4',
                    items : [
                      {
                        name  : 'Some Local Name 5',
                        id    : '123456',
                        data  : null,
                        child : {
                          level : 'Level 5',
                          items : [
                            {
                              name  : 'Some Product Name 8',
                              id    : '1234567',
                              data  : [2, 35, 235, 35554, 55554],
                              child : null
                            }
                          ]
                        }
                      }
                    ]
                  }
                }
              ]
            }
          }
        ]
      }
    }
  ]
};

const arr = [];

function getMatch(data, filters) {
  if (data && data.items.length) {
    let _thisItem;

    for (let i = 0, j = data.items.length; i < j; i += 1) {
      _thisItem = data.items[i];

      for (let x = 0, y = filters.length; x < y; x += 1) {
        if (_thisItem.name === filters[x]) {
          arr.push(_thisItem);
        }
      }

      if (_thisItem.child) {
        getMatch(_thisItem.child, filters);
      }
    }
  }
}

const filterList = [
  ['Some Business Name', 'Some Business Name 2'],
  ['Some Sub-Business Name', 'Some Sub-Business Name 2'],
  ['Some Area Name', 'Some Area Name 3'],
  ['Some Local Name 2', 'Some Local Name 4']
];

getMatch(data, [].concat(...filterList));

console.log(arr)

/*
  Output:
    0: {name: "Some Business Name", id: "123", data: null, child: {…}}
    1: {name: "Some Sub-Business Name", id: "1234", data: null, child: {…}}
    2: {name: "Some Area Name", id: "12345", data: null, child: {…}}
    3: {name: "Some Local Name 2", id: "123456", data: null, child: {…}}
    4: {name: "Some Business Name 2", id: "123", data: null, child: {…}}
    5: {name: "Some Area Name 4", id: "12345", data: null, child: {…}}
    6: {name: "Some Local Name 4", id: "123456", data: null, child: {…}}


  Would like to see eventually:
    {
      level : 'Level 1',
      items : [
        {
          name  : 'Some Business Name',
          id    : '123',
          data  : null,
          child : {
            level : 'Level 2',
            items : [
              {
                name  : 'Some Sub-Business Name',
                id    : '1234',
                data  : null,
                child : {
                  level : 'Level 3',
                  items : [
                    {
                      name  : 'Some Area Name',
                      id    : '12345',
                      data  : null,
                      child : {
                        level : 'Level 4',
                        items : [
                          {
                            name  : 'Some Local Name',
                            id    : '123456',
                            data  : null,
                            child : null // removed because only the immediate parent was selected, but not this item
                          },
                          {
                            name  : 'Some Local Name 2',
                            id    : '123456',
                            data  : null,
                            child : {
                              level : 'Level 5',
                              items : [
                                {
                                  name  : 'Some Product Name 3',
                                  id    : '1234567',
                                  data  : [2, 35, 235, 35554, 55554],
                                  child : null
                                },
                                {
                                  name  : 'Some Product Name 4',
                                  id    : '12345678',
                                  data  : [9, 5, 35, 5764, 335],
                                  child : null
                                }
                              ]
                            }
                          }
                        ]
                      }
                    },
                    {
                      name  : 'Some Area Name 2',
                      id    : '12345',
                      data  : null,
                      child : null // removed because only the immediate parent was selected, but not this item
                    }
                  ]
                }
              }
            ]
          }
        },
        {
          name  : 'Some Business Name 2',
          id    : '123',
          data  : null,
          child : {
            level : 'Level 2',
            items : [
              {
                name  : 'Some Sub-Business Name 2',
                id    : '1234',
                data  : null,
                child : {
                  level : 'Level 3',
                  items : [
                    {
                      name  : 'Some Area Name 3',
                      id    : '12345',
                      data  : null,
                      child : {
                        level : 'Level 4',
                        items : [
                          {
                            name  : 'Some Local Name 4',
                            id    : '123456',
                            data  : null,
                            child : {
                              level : 'Level 5',
                              items : [
                                {
                                  name  : 'Some Product Name 6',
                                  id    : '1234567',
                                  data  : [2, 35, 235, 35554, 55554],
                                  child : null
                                },
                                {
                                  name  : 'Some Product Name 7',
                                  id    : '12345678',
                                  data  : [9, 5, 35, 5764, 335],
                                  child : null
                                }
                              ]
                            }
                          }
                        ]
                      }
                    }
                  ]
                }
              },
              {
                name  : 'Some Sub-Business Name 3',
                id    : '1234',
                data  : null,
                child : null // removed because only the immediate parent was selected, but not this item
              }
            ]
          }
        }
      ]
    };
*/

Each level's items can have a large amount of items and I've just shortened the data for this example.

As you can see from the output above, I have the current level with all child items, but I would like to keep the same structure and only remove the children items that are not in the filter list.

Lodash/Underscore/ES2016 is fine. Any help is appreciated, and I thank you in advance for any insight you may have.

I spent some time and refer to others people's code( Shibo Zhao ),write the following code, it can satisfy the condition for query of an element, if you want to search more,after partial query merge objects should be feasible,good luck.(I'm a Chinese and English is terrible,so my code not )

  function findPathBFS(source, goal) {
  const dataSource = JSON.parse(JSON.stringify(source))
  const res = []
  res.push(...dataSource)
  for (let i = 0; i < res.length; i++) {
    res[i].num = i
  }
  for (let i = 0; i < res.length; i++) {
    const curData = res[i]
    if (curData.name === goal) {
      const result = []
      return (function findParent(data) {
        result.unshift({name:data.name,num:data.num});
        if (data.parent) return findParent(data.parent)
        return result
      })(curData)
    }
    if (curData.child) {
      res.push(...curData.child.items.map((d,i) => {
        d.parent = curData;
        d.num = i;
        return d
      }))
    }
  }
  return []
}


let array = findPathBFS(data.items, 'Some Area Name 3');
console.log(array);

const newData = prune();
console.log(newData)

function prune(){
    let count=0;
    const currentData = JSON.parse(JSON.stringify(data));
    const items = currentData.items;
    const arr = array.map(e=>e.num);
    _prune(items,count,arr);
    return currentData;
}
function _prune(items,count,arr){
    const item = JSON.parse(JSON.stringify(items[arr[count]]));
    items.length=1;
    items[0]=item;
    count++;
    if(count<arr.length){
        _prune(items[0].child.items,count,arr)
    }
    if(count===arr.length){            
        if(items[0]&&items[0].child){
            for(let _item in items[0].child.items){
                _item.child=null;
            }
        }
    }
}

I know you solved it already, but let it be here, maybe someone will find it useful:

// not sure why did you need a two-dimensional array here.
const filterList = [
  'Some Business Name',
  'Some Business Name 2',
  'Some Sub-Business Name',
  'Some Sub-Business Name 2',
  'Some Area Name',
  'Some Area Name 3',
  'Some Local Name 2',
  'Some Local Name 4',
];
// filter by name
let arr = _.filterDeep( data,
  (item, key, parentVal, ctx) => {
    return (_.includes(filterList, item.name) ||
      (ctx.parent &&
        _.includes(filterList, _.get(ctx, 'parent.parent.parent.value.name')))
    );
  },
  { tree: { children: 'items' } }
);
// remove empty 'child'
arr = _.filterDeep( arr,
  (val, key) => {
    return key != 'child' || (val !== null && val.items.length > 0);
  },
  { leavesOnly: false, onTrue: { skipChildren: false } }
);

filterDeep is a method from Deepdash - deep extension for Lodash.

Here is a full working test for your case

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