0

Let's say I have a data object:

{
   apple: 3,
   apple-02: { // stuff },
   apple124: 'morestuff',
   banana: 5,
   bananaPhone: 'morestuff',
   cherry: 10,
}

I want to group the keys in that data object by the longest common prefix of those keys:

{
    apple: {
       apple: 3,
       apple-02: { // stuff },
       apple124: 'morestuff',
    },
    banana: {
       banana: 5,
       bananaPhone: 'morestuff',
    },
    cherry: { cherry: 10 }
}

So far I have this but I'm not entirely sure this works or is the best way to do this. If there's a smarter and more accurate way to do this please let me know:

Object.keys(data).reduce((accumulator, key) => {
    const existingKey = Object.keys(accumulator).find(k => key.includes(k))
    if (existingKey) {
        return { ...accumulator, [existingKey]: { ...accumulator[existingKey], [key]: data[key] } }
    }
    return { ...accumulator, [key]: { [key]: data[key] } }
}, {})
noblerare
  • 10,277
  • 23
  • 78
  • 140
  • Your code will only work if one of the keys is the exact substring you are looking for in the others, can you confirm it will always be the case? Also "I'm not entirely sure this works" -> is it giving the intended result for your test case? – Kaddath Aug 22 '23 at 15:28
  • Does this answer your question? [Group strings by longest common starting substring](https://stackoverflow.com/questions/34210034/group-strings-by-longest-common-starting-substring) – 0stone0 Aug 22 '23 at 15:29
  • @Kaddath Yes, one of the keys is the exact substring that I'm looking to group by. This is guaranteed. And yes, it appears to be giving the right answer but I'm wondering if there's a utility or a better way to do this. – noblerare Aug 22 '23 at 15:31

1 Answers1

0

you can improve it with this

function groupKeysByLongestCommonPrefix(obj) {
  const result = {};

  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      const subObj = { [key]: obj[key] };
      let matchedPrefix = null;

      for (const prefix in result) {
        if (key.startsWith(prefix) || prefix.startsWith(key)) {
          matchedPrefix = prefix;
          break;
        }
      }

      if (matchedPrefix) {
        Object.assign(result[matchedPrefix], subObj);
      } else {
        result[key] = subObj;
      }
    }
  }

  return result;
}

const inputObj = {
  apple: 3,
  'apple-02': { stuff: 'more stuff' },
  apple124: 'morestuff',
  banana: 5,
  bananaPhone: 'morestuff',
  cherry: 10,
};

const groupedObj = groupKeysByLongestCommonPrefix(inputObj);
console.log(groupedObj);

first of, your code does the job in a single expression but "this" provides

Clarity: now it is more explicit in terms of iterating through the object and finding the longest common prefix. It clearly defines the process step by step, which can make it easier to understand for other developers.

Performance: The original code breaks out of the loop as soon as it finds a matching prefix, which can be more efficient for large datasets. on the other hand, "your code" uses the "find" function within the reduced loop, which may perform additional iterations.

Maintainability: This solution uses a straightforward for...in loop, making it easy to modify or extend if needed.

Sammy
  • 99
  • 4
  • What is "this"? Code-only answers are discouraged. – 0stone0 Aug 22 '23 at 15:35
  • Thanks for writing this up but it would be helpful if you explained a bit of what it is doing and why this improves upon the code I have in my question. – noblerare Aug 22 '23 at 15:37
  • explained more with the edit – Sammy Aug 22 '23 at 15:47
  • I agree this is more easily understandable and more performant (avoids the overheads of functional approach). But IMO you missed an opportunity: In the OP's code and in this one, it works if the longest substring is always before the others in the object order. I think OP's code will fail if the order changes, yours works but it will group under the first key found. You can easily distinguish the 2 different cases `key.startsWith(prefix)` / `prefix.startsWith(key)` to fix this – Kaddath Aug 22 '23 at 16:04