0

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 ) );
2
  • 2
    What does updateState( obj[prop] ) do? Commented Jan 17, 2020 at 23:02
  • var arr = [...originalArr]; is a shallow copy. Use lodash it has deepClone function but if you insist on not using an external library you can also do: arr = JSON.parse(JSON.stringify(data)) but make sure to read this first: medium.com/@pmzubar/… Commented Jan 17, 2020 at 23:05

4 Answers 4

1

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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

Comments

0

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.

Comments

0

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

1 Comment

@Klaycom Thank you. The only issue with these replies is that you are banking on "members" as a property, whereas there are infinite ones. Perhaps I need to make that clearer. I'll try to adjust these responses.

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.