-1

I was trying to implement a [Wordle][1] assistant in JavaScript. I am new to JS, and I am sure the following is trivial, but maps are still confusing me. Suppose I have:

{
  'fight' => [ '02222', '02222', '02222', '02222', '20000' ],
  'eight' => [ '02222', '02222', '02222', '02222', '10000' ],
  'right' => [ '02222', '02222', '02222', '02222', '10000' ],
  'sight' => [ '02222', '02222', '02222', '02222', '10000' ],
  'night' => [ '02222', '02222', '02222', '02222', '10000' ],
  'ferns' => [ '20000', '01000', '00100', '00001', '00010' ]
}

How can I have the counts of unique items in the following format:

[{
    "fight": [1, 4],
    "eight": [1, 4],
    "right": [1, 4],
    "sight": [1, 4],
    "night": [1, 4],
    "ferns": [1, 1, 1, 1, 1]
}]

(Would I do better with a different data structure?)

(These are calculated values, so I have no idea what the keys of data are going to be, neither how many items each key will have.)

blackened
  • 861
  • 10
  • 19
  • 2
    Why do you have one-element arrays everywhere instead of just objects? – Konrad Oct 23 '22 at 20:16
  • Does this answer your question? [Most efficient method to groupby on an array of objects](https://stackoverflow.com/questions/14446511/most-efficient-method-to-groupby-on-an-array-of-objects) – pilchard Oct 23 '22 at 20:46
  • This is just a 'group-by'. (also the first value in the array is just the `length` of the array of values, so doesn't need to be stored). – pilchard Oct 23 '22 at 20:46
  • Actually, since you have no duplicate keys it can be handled in just a `map()` – pilchard Oct 23 '22 at 20:52

1 Answers1

1

Edit

To remap your updated data is simpler in many ways. I'm assuming your syntax indicates that you are using a Map, and since a Map can not have duplicate keys you needn't 'group-by' but simply iterate over the entries of the map transforming each values array by counting the non-zero characters in each string. Since you also want to remove duplicate counts ([1, 4] and not [1, 4, 4, 4, 4]) you can create an intermediate Set of each values array before spreading it back into an array and then mapping it to count characters.

const input = new Map([['fight', ['02222', '02222', '02222', '02222', '20000']], ['eight', ['02222', '02222', '02222', '02222', '10000']], ['right', ['02222', '02222', '02222', '02222', '10000']], ['sight', ['02222', '02222', '02222', '02222', '10000']], ['night', ['02222', '02222', '02222', '02222', '10000']], ['ferns', ['20000', '01000', '00100', '00001', '00010']]]);

// Map(6) {
//   'fight' => [ '02222', '02222', '02222', '02222', '20000' ],
//   'eight' => [ '02222', '02222', '02222', '02222', '10000' ],
//   'right' => [ '02222', '02222', '02222', '02222', '10000' ],
//   'sight' => [ '02222', '02222', '02222', '02222', '10000' ],
//   'night' => [ '02222', '02222', '02222', '02222', '10000' ],
//   'ferns' => [ '20000', '01000', '00100', '00001', '00010' ]
// }

const result = {};

for (const [key, values] of input.entries()) {
  result[key] = [...new Set(values)].map(s => s.match(/[^0]/g).length)
}

console.log(result)

I'm using a String.match() with a regex here to count non-zero digits in the string, but there are plenty of alternatives, here are two off the top of my head:

// String#replaceAll()
'02222'.replaceAll('0', '').length;

// String#split() + filter()
'02222'.split('').filter(c => c !== '0').length;

Original answer

Your data sample leaves a lot of questions both about what shape the input could be as well as what you would like the output to be. But regardless it seems that a fairly standard 'group-by' will work.

const data = [{ "fight": [{ "20000": 1, "02222": 4 }], "eight": [{ "10000": 1, "02222": 4 }], "right": [{ "20000": 1, "02222": 4 }], "sight": [{ "20000": 1, "02222": 4 }], "night": [{ "20000": 1, "02222": 4 }], "ferns": [{ "20000": 1, "01000": 1, "00100": 1, "00001": 1, "00010": 1 }] }];

const result = {};

for (const datum of data) {
  for (const [k, v] of Object.entries(datum)) {
    const values = v.flatMap(Object.values);
    (result[k] ??= [0]).push(...values);
    result[k][0] += values.length; // <-- This doesn't need to be stored, it is just the array.length (not including this element)
  }
}

console.log(result);

The above assumes that you might have duplicate keys, and flattens the result into a single object. If data will contain multiple objects and you want to maintain the grouped results within those bounds you'll need to group per iteration. (The inner logic remains the same)

const data = [
  {
    "fight": [
      { "20000": 1, "02222": 4 },
      { "20000": 1, "01000": 1, "00100": 1, "00001": 1, "00010": 1 }
    ]
  },
  {
    "sight": [
      { "20000": 1, "02222": 4 }
    ],
    "night": [
      { "20000": 1, "02222": 4 },
      { "20000": 1, "02222": 4 }
    ]
  }
];

const result = [];

for (const datum of data) {
  const group = {}
  for (const [k, v] of Object.entries(datum)) {
    const values = v.flatMap(Object.values);
    (group[k] ??= [0]).push(...values);
    group[k][0] += values.length;
  }
  result.push(group)
}

console.log(result);
pilchard
  • 12,414
  • 5
  • 11
  • 23
  • Do you have en example of what the data would look like? – pilchard Oct 24 '22 at 07:43
  • Without knowing how you are using the result it's hard to say what shape will work better for you. I would say to avoid storing implicit information (such as the length of the concatenated array in the above examples) as this can always be retrieved. In the case of your sequence objects `{20000: 1, 02222: 4}` you could just as well store just the strings `['20000', '02222']` and since the number value is just the count of non-zero characters. – pilchard Oct 24 '22 at 08:10
  • updated to address you new data – pilchard Oct 24 '22 at 23:47