skip to Main Content

I’m trying to compare the current state of selected options against an incoming state of options. The incoming state will have property selected which is a boolean.

What I’m trying to accomplish is this: If an item in the incomingOptions has selected: true, update the same item in currentState or add it to the current state if it doesn’t exist. If selected: false, the item should not be returned in the new state.

const currentState = {
  products: [
    {
      optionid: 808541,
      quantity: 7
    },
    {
      optionid: 808542,
      quantity: 7
    },
    {
      optionid: 808543,
      quantity: 6
    },
    {
      optionid: 808546,
      quantity: 7
    },
    {
      optionid: 808547,
      quantity: 7
    },
    {
      optionid: 808548,
      quantity: 7
    },
    {
      optionid: 808549,
      quantity: 1
    },
    {
      optionid: 808550,
      quantity: 7
    },
    {
      optionid: 808551,
      quantity: 7
    },
    {
      optionid: 808552,
      quantity: 7
    },
    {
      optionid: 808553,
      quantity: 7
    }
  ]
};
const incomingOptions = [
  {
    id: 808539, // this item is new
    selected: true,
    quantity: 5
  },
  {
    id: 808540, // this item is new
    selected: true,
    quantity: 5
  },
  {
    id: 808541, // this item exists in current state, but is now false
    selected: false,
    quantity: 5
  },
  {
    id: 808542, // this item exists in current state, but the quantity is now 10
    selected: true,
    quantity: 10
  },
  {
    id: 808543,
    selected: false
  },
  {
    id: 808544,
    selected: false
  },
  {
    id: 808545,
    selected: false
  }
];

The final state I’m after is this:

products: [
    {
       "optionid": 808542,
       "quantity": 10
    },
    {
        "optionid": 808546,
        "quantity": 7
    },
    {
        "optionid": 808547,
        "quantity": 7
    },
    {
        "optionid": 808548,
        "quantity": 7
    },
    {
        "optionid": 808549,
        "quantity": 1
    },
    {
        "optionid": 808550,
        "quantity": 7
    },
    {
        "optionid": 808551,
        "quantity": 7
    },
    {
        "optionid": 808552,
        "quantity": 7
    },
    {
        "optionid": 808553,
        "quantity": 7
    },
    {
        "optionid": 808539,
        "quantity": 5
    },
    {
        "optionid": 808540,
        "quantity": 5
    }
]

I’ve tried using reduce with a combination of findIndex, but I’m finding that I’m needing to loop things over several times and it feels really inefficient.

Forgot my first pass, which updates the current items, but not any new items:

const updatedState = state.products.reduce((acc, curr) => {
  const option = currentOptions.find((opt) => opt.id === curr.optionid);
  if (option) {
    return option.selected
      ? 
        [
          ...acc,
          {
            ...curr,
            quantity: option.quantity
          }
        ]
      : acc;
  }
  return [...acc, curr];
}, []);

2

Answers


  1. If you’re able to slightly modify the input data:

    const currentState = {
      products: [
        {
          optionid: 808541,
          quantity: 7
        },
    

    — it can be simplified to:

    const currentState = {
      808541: 7,
    

    Given the above you can just use a simple, readable, easily debuggable forEach like:

    const currentState = {
      808541: 7, 808542: 7, 808543: 6,
      808546: 7, 808547: 7, 808548: 7,
      808549: 1, 808550: 7, 808551: 7,
      808552: 7, 808553: 7,
    };
    
    const incomingOptions = [
      {id: 808539, selected: true, quantity: 5},
      {id: 808540, selected: true, quantity: 5},
      {id: 808541, selected: false, quantity: 5},
      {id: 808542, selected: true, quantity: 10},
      {id: 808543, selected: false},
      {id: 808544, selected: false},
      {id: 808545, selected: false}
    ];
    
    const res = {...currentState}; // Shallow clone
    
    incomingOptions.forEach((opt) => {
      if (opt.selected) res[opt.id] = opt.quantity;
      else delete res[opt.id];
    });
    
    console.log(res);

    Readable, reasonable, and developer friendly.
    If you really want to stick to something more compact (no reason to), you can always do:

    incomingOptions.forEach(opt => 
      opt.selected ? res[opt.id] = opt.quantity : delete res[opt.id]
    );
    
    Login or Signup to reply.
  2. It works for me:

    const currentState = {
      products: [
        {
          optionid: 808541,
          quantity: 7
        },
        {
          optionid: 808542,
          quantity: 7
        },
        {
          optionid: 808543,
          quantity: 6
        },
        {
          optionid: 808546,
          quantity: 7
        },
        {
          optionid: 808547,
          quantity: 7
        },
        {
          optionid: 808548,
          quantity: 7
        },
        {
          optionid: 808549,
          quantity: 1
        },
        {
          optionid: 808550,
          quantity: 7
        },
        {
          optionid: 808551,
          quantity: 7
        },
        {
          optionid: 808552,
          quantity: 7
        },
        {
          optionid: 808553,
          quantity: 7
        }
      ]
    };
    
    const incomingOptions = [
      {
        id: 808539, // this item is new
        selected: true,
        quantity: 5
      },
      {
        id: 808540, // this item is new
        selected: true,
        quantity: 5
      },
      {
        id: 808541, // this item exists in current state, but is now false
        selected: false,
        quantity: 5
      },
      {
        id: 808542, // this item exists in current state, but the quantity is now 10
        selected: true,
        quantity: 10
      },
      {
        id: 808543,
        selected: false
      },
      {
        id: 808544,
        selected: false
      },
      {
        id: 808545,
        selected: false
      }
    ];
    
    const removeItems = incomingOptions.filter(item => !item.selected).map(item => item.id);
    
    const filterCurrent = currentState.products.filter(item => removeItems.includes(item.optionid) === false);
    const filterIncomming = incomingOptions.filter(item => item.selected);
    
    const result = {};
    
    filterCurrent.forEach(({ optionid, quantity }) => {
      const find = filterIncomming.find(value => value.id === optionid);
      result[optionid] = find ? { optionid, quantity: find.quantity } : { optionid, quantity };
    });
    
    filterIncomming.forEach(item => {
      result[item.id] ??= { optionid: item.id, quantity: item.quantity };
    })
    
    
    
    console.log({products: Object.values(result)});
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search