Array of objects: return only objects (and children) where attribute matches

385 Views Asked by At

I have an array with several "levels" or children. I would like to create a new array of objects where archived===false so that no archived objects are listed.

So this...

var arr = [
    {"id":1, "archived":false,"children":[
        {"id":1,"archived":true, "subs":[
            {"id":1,"archived":true},
            {"id":2,"archived":true},
            {"id":3,"archived":true}
        ]},
        {"id":2,"archived":false, "subs":[
            {"id":1,"archived":false},
            {"id":2,"archived":false},
            {"id":3,"archived":true}
        ]}
    ]},
    {"id":2, "archived":true,"children":[
        {"id":1,"archived":true, "subs":[
            {"id":1,"archived":true},
            {"id":2,"archived":true},
            {"id":3,"archived":true}
        ]},
        {"id":2,"archived":true, "subs":[
            {"id":1,"archived":true},
            {"id":2,"archived":true},
            {"id":3,"archived":true}
        ]}
    ]},
    {"id":3, "archived":false,"children":[
        {"id":1,"archived":false, "subs":[
            {"id":1,"archived":true},
            {"id":2,"archived":false},
            {"id":3,"archived":true}
        ]},
        {"id":2,"archived":false, "subs":[
            {"id":1,"archived":false},
            {"id":2,"archived":false},
            {"id":3,"archived":false}
        ]}
    ]}
];

Becomes...

var arr = [
    {"id":1, "archived":false,"children":[
        {"id":2,"archived":false, "subs":[
            {"id":1,"archived":false},
            {"id":2,"archived":false}
        ]}
    ]},
    {"id":3, "archived":false,"children":[
        {"id":1,"archived":false, "subs":[
            {"id":2,"archived":false}
        ]},
        {"id":2,"archived":false, "subs":[
            {"id":1,"archived":false},
            {"id":2,"archived":false},
            {"id":3,"archived":false}
        ]}
    ]}
];

I've tried combinations of map, reduce, filter, and grep...but I probably do not have the order right, etc. I know I could just loop through each level, but I can't get it just right.

It would be great to have this contained in a linq.js enumerable array or in jQuery.

Non-working Example fiddle

3

There are 3 best solutions below

4
On BEST ANSWER

A well crafted function to handle recursion could make doing this with linq.js fairly simple to do. This in particular doesn't necessarily need linq.js, but it keeps it simple.

function isNotArchived(childrenName, childrenFilter) {
  return function (e) {
    var filtered = e.Where("!$.archived");
    if (childrenName && childrenFilter) {
      return filtered.Select(function (c) {
        var result = { id: c.id, archived: c.archived };
        result[childrenName] = $.Enumerable.From(c[childrenName])
          .Let(childrenFilter)
          .ToArray();
        return result;
      });
    }
    return filtered;
  };
}
var filtered = $.Enumerable.From(arr)
  .Let(isNotArchived("children",
    isNotArchived("subs",
      isNotArchived()
    )
  ))
  .ToArray();

fiddle

7
On

This requires recursion.

Change your code to include a function that works along the lines of this pseudo code as:

function filterRecursive(array, filterFunction){
    for(obj in array){
        if(obj.children){
            obj.children = filterRecursive(obj.children, filterFunction);
        }
        obj = filterFunction(obj);
    }
}
0
On

My solution with no recursion and assuming the depth and structure of the initial object is fixed:

var arr = [
  {
    "id": 1, "archived": false, "children": [
      {
        "id": 1, "archived": true, "subs": [
          {"id": 1, "archived": true},
          {"id": 2, "archived": true},
          {"id": 3, "archived": true}
        ]
      },
      {
        "id": 2, "archived": false, "subs": [
          {"id": 1, "archived": false},
          {"id": 2, "archived": false},
          {"id": 3, "archived": true}
        ]
      }
    ]
  },
  {
    "id": 2, "archived": true, "children": [
      {
        "id": 1, "archived": true, "subs": [
          {"id": 1, "archived": true},
          {"id": 2, "archived": true},
          {"id": 3, "archived": true}
        ]
      },
      {
        "id": 2, "archived": true, "subs": [
          {"id": 1, "archived": true},
          {"id": 2, "archived": true},
          {"id": 3, "archived": true}
        ]
      }
    ]
  },
  {
    "id": 3, "archived": false, "children": [
      {
        "id": 1, "archived": false, "subs": [
          {"id": 1, "archived": true},
          {"id": 2, "archived": false},
          {"id": 3, "archived": true}
        ]
      },
      {
        "id": 2, "archived": false, "subs": [
          {"id": 1, "archived": false},
          {"id": 2, "archived": false},
          {"id": 3, "archived": false}
        ]
      }
    ]
  }
];

function checkChildrensForFalse(elements) {
  var retVal = [];
  for (var eleChildIndex in elements) {
    if (elements[eleChildIndex].archived == false) {
      var tmpObj = {
        'id': elements[eleChildIndex].id,
        'archived': elements[eleChildIndex].archived,
        'subs': []
      }
      for (var eleSubIndex in elements[eleChildIndex].subs) {
        if (elements[eleChildIndex].subs[eleSubIndex].archived == false) {
          tmpObj.subs.push(elements[eleChildIndex].subs[eleSubIndex]);
        }
      }
      if (tmpObj.subs !== []) {
        retVal.push(tmpObj);
      }
    }
  }
  return retVal;
}

arr = arr.map(function (currentValue, index, array) {
  if (currentValue.archived == false) {
    var ele = checkChildrensForFalse(currentValue.children);
    if (ele !== []) {
      return {'id': currentValue.id, 'archived': currentValue.archived, 'children': ele};
    }
  } else {
    return null;
  }
}).filter(function (item) {
  return item != null
});

/**********
         * The result formatted is:
        var result = [{
            "id": 1, "archived": false,"children": [{
                "id": 2, "archived": false, "subs": [
                    {"id": 1, "archived": false}, {"id": 2, "archived": false}]
            }]
        }, {"id": 3, "archived": false, "children": [
            {"id": 1, "archived": false, "subs": [{"id": 2, "archived": false}]},
            {"id": 2, "archived": false, "subs": [{"id": 1, "archived": false}, {"id": 2, "archived": false}, {"id": 3, "archived": false}]
            }]
        }];
         */
document.write('<p>' + JSON.stringify(arr) + '</p>')