1
\$\begingroup\$

I have a method that merges keys and indexes from an array into an object.

I'm stuck with ways to compress this method, and I don't know what I can do to make it simpler.

Goal

  • get an array of objects with keys
  • return an object with a unique key and the matching indexes

Code (simplified)

const items = [
  {
    "key": 0
  },
  {
    "key": 2
  },
  {
    "key": 4
  },
  {
    "key": 4
  }
]

function mergeItems (items) {
  const helperObj = {}
  // loop over items
  items.forEach((item, itemIdx) => {
    const { key } = item
    // got key in helper obj? push index
    if (key in helperObj) {
      helperObj[key].push(itemIdx)
    } else {
      // create new array and set index
      helperObj[key] = [itemIdx]
    }
  });
  return helperObj
}

console.log(mergeItems(items));

Expected output:

{
  "0": [0],
  "2": [1],
  "4": [2,3]
}

Question

Is there a way to do this without creating a helper object?

\$\endgroup\$
2
  • 1
    \$\begingroup\$ I wouldn't consider that an "helper object". You have a function that is supposed to return a (new) object. You have to create that object sometime, that can't be avoided. My "solution" would be rename the variable to what it is: result. \$\endgroup\$
    – RoToRa
    Jan 24, 2023 at 12:00
  • \$\begingroup\$ Thanks @RoToRa, You're right. \$\endgroup\$ Jan 24, 2023 at 12:38

1 Answer 1

2
\$\begingroup\$

Is there a way to do this without creating a helper object?

It may be possible. Perhaps what you are really asking is whether there is a way to have a pure function, such that helperObj does not need to be declared outside the callback function and modified within the callback function before it is utilized after the forEach method is called. The callback functions passed to the forEach method typically end up not being pure for this reason.

Instead of using the forEach() method, the reduce() method can be used. It still may have a helperObj for the accumulator argument but the scope is limited to within the callback function.

const items = [{
    "key": 0
  },
  {
    "key": 2
  },
  {
    "key": 4
  },
  {
    "key": 4
  }
]
const mergedItems = items.reduce(function(helperObj, { key }, index) {
  if (helperObj[key]) {
    helperObj[key].push(index);
  } else {
    helperObj[key] = [index];
  }
  return helperObj;
}, {});
console.log('merged items: ', mergedItems);

Instead of either pushing or assigning to an array the array could be created when it does not exist:

const items = [{
    "key": 0
  },
  {
    "key": 2
  },
  {
    "key": 4
  },
  {
    "key": 4
  }
]
const mergedItems = items.reduce(function(helperObj, { key }, index) {
  if (!helperObj[key]) {
    helperObj[key] = [];
  }
  helperObj[key].push(index);
  return helperObj;
}, {});
console.log('merged items: ', mergedItems);

And if the goal is to greatly simplify it, a ternary operator could be used to compress it even further:

const items = [{
    "key": 0
  },
  {
    "key": 2
  },
  {
    "key": 4
  },
  {
    "key": 4
  }
]
const mergedItems = items.reduce((acc, { key }, index) => { acc[index] ? acc[index].push(key) : acc[index] = [key]; return acc}, {})
console.log('merged items: ', mergedItems);

\$\endgroup\$
1
  • \$\begingroup\$ Thanks. Let me think about this and I will come back later! \$\endgroup\$ Jan 23, 2023 at 19:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.