sandraqu
sandraqu

Reputation: 1528

Recursive Array Modification

I would like to copy an array so as not to modify the original, and remove all selected: false from new array, and return this new array. The array is infinitely nested, and with infinite property names non of which are predictable, so this should be possible through iteration looking at the value of each property for Array.isArray(). While I can remove selected:false objects in the iteration, I fail to return the modified array back to the new array.

function failing to filter aliens. Also, function works in CodePen, but not in my code.

// sample nested data
var data = [
  {
    partyId: "animal-ID-001",
    selected: false,
    members: [
      {
        selected: false,
        userId: "animal-user-3443"
      },
      {
        selected: false,
        userId: "animal-user-3444"
      }
    ]
  },
  {
    partyId: "benjamin-ID-002",
    selected: true,
    members: [
      {
        selected: true,
        userId: "benjamin-user-5567",
        teams: [
          {
           selected: true,
           teamId: "team-benjamin-678"
          },
          {
            selected: false,
            teamId: "team-benjamin-3468"
          }
      ]},
      {
        selected: false,
        userId: "benjamin-user-55671"
      }
    ]
  },
  {
    partyId: "crystal-ID-003",
    selected: true,
    members: [
      {
        selected: true,
        userId: "crystal-user-8567"
      },
      {
        selected: true,
        userId: "crystal-user-85671"
      }
    ],
    aliens: [
      {
        selected: false,
        alienId: "crystal-alien-467"
      },
      {
        selected: false,
        alienId: "crystal-alien-230"
      }
    ]
  }
];

// remove selected false from array
// updated per suggestions

function updateState(arr) {
        return arr.filter(obj => obj.selected ).map( obj => {
            for (var prop in obj) {
                if( Array.isArray( obj[prop] ) ) {
                    return { ...obj, [prop]: updateState( obj[prop] ) };
                }
            }
            return { ...obj }
        });
    }

console.log( updateState( data ) );

Upvotes: 0

Views: 73

Answers (4)

Scott Sauyet
Scott Sauyet

Reputation: 50787

Does something like this work for you?:

const removeNonselected = (x) => 
  Array .isArray (x)
    ? x .reduce (
        (all, item) => item .selected === false 
          ? all 
          : all .concat (removeNonselected (item)), 
        []
      )
  : typeof x == 'object'
    ? Object .fromEntries (
        Object .entries (x) .map(([n, v]) => [n, removeNonselected(v)])
      ) 
  : x

const data = [{partyId: "animal-ID-001", selected: false, members: [{selected: false, userId: "animal-user-3443"}, {selected: false, userId: "animal-user-3444"}]}, {partyId: "benjamin-ID-002", selected: true, members: [{selected: true, userId: "benjamin-user-5567", teams: [{selected: true, teamId: "team-benjamin-678"}, {selected: false, teamId: "team-benjamin-3468"}]}, {selected: false, userId: "benjamin-user-55671"}]}, {partyId: "crystal-ID-003", selected: true, members: [{selected: true, userId: "crystal-user-8567"}, {selected: true, userId: "crystal-user-85671"}], aliens: [{selected: false, alienId: "crystal-alien-467"}, {selected: false, alienId: "crystal-alien-230"}]}];

console .log (removeNonselected (data))
console .log ('original data unchanged:')
console .log (data)

This handles three cases: where the data is an array, where it's an object, or where it's something else. For an array we keep only the selected ones (where selected is not false) and recurs on those values. For an object, we keep other values intact, but recur on array properties. Anything else we just return as is.

This does not remove a selected: false property of an object, only from within an array. It would not be much harder to add that, but it didn't seem to be in your requirements.

If you environment doesn't support Object.fromEntries, it's fairly easy to shim.

Upvotes: 1

Klaycon
Klaycon

Reputation: 11080

Don't reinvent the wheel, check what functions you can use on arrays. Array.filter with recursion is perfect here:

var data=[{partyId:"animal-ID-001",selected:!1,members:[{selected:!1,userId:"animal-user-3443"},{selected:!1,userId:"animal-user-3444"}]},{partyId:"benjamin-ID-002",selected:!0,members:[{selected:!0,userId:"benjamin-user-5567"},{selected:!1,userId:"benjamin-user-55671"}]},{partyId:"crystal-ID-003",selected:!0,members:[{selected:!0,userId:"crystal-user-8567"},{selected:!0,userId:"crystal-user-85671"}]}];

function removeNonselected(arr) {
  return arr.filter(obj => obj.selected).map(obj => {
    if(obj.members) return { ...obj, members: removeNonselected(obj.members) };
    else return { ...obj }
  });
}

console.log(removeNonselected(data));

Upvotes: 0

evhenious
evhenious

Reputation: 51

Try this simple one:

let selectedParties = data.filter(item => item.selected);
let partiesWithSelectedMembersOnly = selectedParties.map(item => {
  return {
    ...item,
    members: item.members.filter(member => member.selected)
  };
});

Array.filter() returns new array, so you will not modify initial one.

Upvotes: 0

CertainPerformance
CertainPerformance

Reputation: 370689

First .filter the original data, removing items with selected: false, then .map the result, and inside the callback, return the same object while .filtering the members property. Then

var data = [{
    partyId: "animal-ID-001",
    selected: false,
    members: [{
        selected: false,
        userId: "animal-user-3443"
      },
      {
        selected: false,
        userId: "animal-user-3444"
      }
    ]
  },
  {
    partyId: "benjamin-ID-002",
    selected: true,
    members: [{
        selected: true,
        userId: "benjamin-user-5567"
      },
      {
        selected: false,
        userId: "benjamin-user-55671"
      }
    ]
  },
  {
    partyId: "crystal-ID-003",
    selected: true,
    members: [{
        selected: true,
        userId: "crystal-user-8567"
      },
      {
        selected: true,
        userId: "crystal-user-85671"
      }
    ]
  }
];
const updatedData = data
  .filter(({ selected }) => selected)
  .map(({ members, ...rest }) => ({
    ...rest,
    members: members.filter(({ selected }) => selected)
  }));
console.log(updatedData);

Upvotes: 1

Related Questions