3
\$\begingroup\$

I am writing a function to decode data from a run length encoded format like:


{
  dataGroup: {
    count: [4, 4, 2, 1],
      dataOne: [1, 3, 2, 1],
      dataTwo: [7, -1, 9, 0],
      dataThree: [3, 8, 1, 2]
  }
}

to

{
  groupName: {
    dataOne:   [1, 1, 1, 1, 3, 3, 3, 3, 2, 2, 1],
    dataTwo:   [7, 7, 7, 7,-1,-1,-1,-1, 9, 9, 0],
    dataThree: [3, 3, 3, 3, 8, 8, 8, 8, 1, 1, 2]
  }
}

I have written two solutions to date but feel like there may yet be a better way.

function decode(obj) {

    const arrayLengthN = (N, data=null) => [...Array(N)].map(_ => data);

    const [ group ] = Object.keys(obj);
    const { count, ...data } = obj[group];
    const dataKeys = Object.keys(data);

    // method one

    // output object to be written upon iteration over count and data
    const output = {};

    // populate output with empty arrays to push to
    dataKeys.map(key => output[key] = []);

    // do the decoding
    count
        .map((n, i) => {
            dataKeys
                .map(key => {
                    output[key].push(...arrayLengthN(n, data[key][i]));
                });
        });

    // method two

    const otherMethod = {
        [group]: Object.assign(
            {},
            ...dataKeys.map((key) => {
                return {
                    [key] : count
                        .map((n, i) => dataKeys
                            .map(key => arrayLengthN(n, data[key][i]))
                        )
                        .flat(2)
                };
            })
        )};

    return {[group]: output}
    // or return otherMethod
}

Is there anything wrong with the first method? It feels clunky to create an object, add empty arrays to it, and then mutate that object from within a .map call. That being said, to me it seems far more readable.

Any help or comments much appreciated.

Cheers,

P

\$\endgroup\$
1
\$\begingroup\$

May i suggest a shorter code?

var data = {
  dataGroup: {
    count: [4, 4, 2, 1],
      dataOne: [1, 3, 2, 1],
      dataTwo: [7, -1, 9, 0],
      dataThree: [3, 8, 1, 2]
  }
}

var processedData = Object.keys(data.dataGroup).filter(key => key != 'count').reduce((acc, key) => {
  acc.groupName[key] = [];
  data.dataGroup.count.forEach((amount, index) => {
    for (let i = 0; i < amount; i++) {
      acc.groupName[key].push(data.dataGroup[key][index]);
    }
  });
  return acc;
}, { groupName: {}});
console.log(processedData);

\$\endgroup\$
  • \$\begingroup\$ Offering an alternate solution isn't really considered giving a good answer, and "Your code is too long" might not really be an insightful observation. Could you discuss why you think the code is too long? Please read codereview.stackexchange.com/help/how-to-answer \$\endgroup\$ – pacmaninbw Jun 6 at 13:02
  • \$\begingroup\$ Hi, thanks for your solution. One thing to point out is that you've fixed the key 'groupName' in your output. For my purposes, the group key must be matched to the input object's group key. \$\endgroup\$ – Batters Jun 6 at 13:32
0
\$\begingroup\$

A bit late but looks good to me. You could use Array.prototype.fill instead of arrayLengthN, and perhaps a reducer as the function but that’s a matter of taste:

// assignment as expression to avoid explicit returns
set = (obj, key, value) => (obj[key] = value, obj)

decode = (output, [group, {count, ...entries}]) =>
  set(output, group, Object.entries(entries).reduce((acc, [key, values]) =>
    set(acc, key, [].concat(...values.map((n, i) => Array(count[i]).fill(n))))
  , {}))

so you could Object.entries(data).reduce(decode, {}).

I used concat because in my node version there wasn’t support for flat.

I like this because all the “variable” names are argument names (although that’s bit of a moot point).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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