Search code examples
javascriptarraysalgorithmfiltersplice

Based on a condition, how to remove an item from the processed array and move it to another array?


I have two arrays in my react component as follows ...

const [Selected, setSelected] = React.useState([]);
const [NonSelected, setNonSelected] = React.useState([]);

const Tmp_Selected = [
  { "Metric": "AAA", "Weight": 10, "Value": "xxx" },
  { "Metric": "BBB", "Weight": 20, "Value": "xx1" },
  { "Metric": "CCC", "Weight": 30, "Value": "xx2" },
];
const Tmp_NonSelected = [
  { "Metric": "DDD", "Weight": 5, "Value": "yy" },
  { "Metric": "EEE", "Weight": 15, "Value": "zz" },
  { "Metric": "FFF", "Weight": 25, "Value": "cc" },
];

React.useEffect(() => {
  setSelected(Tmp_Selected);
  setNonSelected(Tmp_NonSelected);
}, [location]);

There is a function which gets passed just Metric and Weight related values. Based on that I need to move each object which matches both criteria from the NonSelected to the Selected array.

const MoveValues = (Metric='DDD', Weight=5) => {
  
}

The expected result for the above two default measures is equal to this ...

const Tmp_Selected = [
   { "Metric": "AAA", "Weight": 10, "Value": "xxx" },
   { "Metric": "BBB", "Weight": 20, "Value": "xx1" },
   { "Metric": "CCC", "Weight": 30, "Value": "xx2" },
   { "Metric": "DDD", "Weight": 5, "Value": "yy" },
];
const Tmp_NonSelected = [
   { "Metric": "EEE", "Weight": 15, "Value": "zz" },
   { "Metric": "FFF", "Weight": 25, "Value": "cc" },
];

Solution

  • The feature the OP actually relies on for solving the task is an array mutating reject functionality.

    Such a function (or method) does accept a callback similar to filter, where the callback's parameters are item, idx, arr, ..., and its return value is expected to be a boolean type or at least truthy/falsy. Upon the result of each invocation of the callback, while iterating the to be operated array entirely, a reject implementation will splice each matching item from the operated array, thus mutating the latter. Each spliced item gets collected in the implementation's result array which also is the reject function's return value.

    Thus, a possible implementation of the OP's moveValues function can be accomplished as easy as this ...

    const tmpSelected = [
      { "Metric": "AAA", "Weight": 10, "Value": "xxx" },
      { "Metric": "BBB", "Weight": 20, "Value": "xx1" },
      { "Metric": "CCC", "Weight": 30, "Value": "xx2" },
    ];
    const tmpNonSelected = [
      { "Metric": "DDD", "Weight": 5, "Value": "yy" },
      { "Metric": "EEE", "Weight": 15, "Value": "zz" },
      { "Metric": "DDD", "Weight": 5, "Value": "aa" },
      { "Metric": "FFF", "Weight": 25, "Value": "cc" },
      { "Metric": "DDD", "Weight": 5, "Value": "bb" },
    ];
    
    const moveItems = (metric, weight) => {
      tmpSelected
        .push(
          ...reject(tmpNonSelected, item => {
    
            return item.Metric === metric && item.Weight === weight;
          }),
        );
    };
    moveItems('DDD', 5);
    
    console.log({ tmpSelected, tmpNonSelected });
    .as-console-wrapper { min-height: 100%!important; top: 0; }
    <script>
    function reject(target, condition) {
      const result = [];
    
      let idx = target.length;
      const copy = [...target];
    
      // - Processing the `target` array from RIGHT to LEFT
      //   keeps the `idx` always in sync with both related
      //   array items, the one of the mutated and also the
      //   one of the unmutated `copy` of the processed array
      //   reference.
      // - Thus, `condition` always gets passed the unmutated
      //   shallow `copy`.
    
      while (idx) {
        if (
          // - take a *sparse array* into account.
          target.hasOwnProperty(--idx) &&
    
          // - keep processing the unmutated array.
          condition(copy[idx], idx, copy)
        ) {
          // - keep filling the `result` array with each
          //   *rejected* item FROM its LEFT side while
          //   mutating the `target` array.
          result.unshift(target.splice(idx, 1)[0]);
        }
      }
      // - returns an array of rejected items, but not the
      //   processed and mutated `target` array reference.
      return result;
    }
    </script>