3

I'm trying to solve this problem in many ways, but unable to move further.

I've a data something like this:

const dummyData = [{
    metric: {
        id: "A",
        code: "00",
        advice: "123"
    },
    values: [[123, 1], [332, 2]]
}, {
    metric: {
        id: "A",
        code: "01",
        advice: "123"
    },
    values: [[123, 5], [332, 3]]
}, {
    metric: {
        id: "B",
        code: "01",
        advice: "123"
    },
    values: [[123, 3]]
}]

I wanted this in 3 different formats with various group by:

  • Group by id, and sum the values[1] For example:

    [{
        id: 'A'
        values: [{
            valueId: 123,
            value: 6    // 1 + 5
        }, {
            valueId: 332,
            value: 5    // 2 + 3
        }]
    }, {
        id: 'B'
        values: [{
            valueId: 123,
            value: 3
        }]
    }]
    
  • Group by id and code, then sum the values[1]

    [{
        id: 'A'
        code: '00'
        values: [{
            valueId: 123,
            value: 1
        }, {
            valueId: 332,
            value: 2
        }]
    }, {
        id: 'A'
        code: '01'
        values: [{
            valueId: 123,
            value: 5
        }, {
            valueId: 332,
            value: 3
        }]
    }, {
        id: 'B'
        code: '01'
        values: [{
            valueId: 123,
            value: 3
        }]
    }]
    
  • Group by id and advice, then sum the values[1]

    [{
        id: 'A'
        advice: '123'
        values: [{
            valueId: 123,
            value: 6    // 1 + 5
        }, {
            valueId: 332,
            value: 5    // 2 + 3
        }]
    }, {
        id: 'B'
        advice: '123'
        values: [{
            valueId: 123,
            value: 3
        }]
    }]
    

So far I've managed to do some simple group by with a single key like this:

const a = dummyData.reduce((accumulator, currentValue) => {
    (accumulator[currentValue.metric["id"]] = accumulator[currentValue.metric["id"]] || []).push(currentValue.values)
    return accumulator
}, {})

Though it is not in my expected format. I know that I've to use reduce to achieve this, however I'm unable to make it work or I don't know how to do.

2 Answers 2

1

You could group with an object as has table and fin the nested values for grouping.

This approach uses a double grouping, one for the outer groups with the wanted properties and another for the valueId/value.

The outer grouping takes a joint key with the values for grouping, separated by a |. I generates a new object with the given keys and an empty array for the values.

The inner grouping takes place with the valueId and a seach for an object with this values. If not found, a new object is added to the values array.

Finally the value is added to the specific group.

function groupBy(data, keys) {
    return Object.values(data.reduce((r, { metric, values }) => {
        const key = keys.map(k => metric[k]).join('|');
        r[key] = r[key] || { ...Object.fromEntries(keys.map(k => [k, metric[k]])), values: [] };
        values.forEach(([valueId, value]) => {
            let temp = r[key].values.find(q => q.valueId === valueId);
            if (!temp) r[key].values.push(temp = { valueId, value: 0 });
            temp.value += value;
        });
        return r;
    }, {}));
}

const
    dummyData = [{ metric: { id: "A", code: "00", advice: "123" }, values: [[123, 1], [332, 2]] }, { metric: { id: "A", code: "01", advice: "123" }, values: [[123, 5], [332, 3]] }, { metric: { id: "B", code: "01", advice: "123" }, values: [[123, 3]] }];

console.log(groupBy(dummyData, ['id']));
console.log(groupBy(dummyData, ['id', 'code']));
console.log(groupBy(dummyData, ['id', 'advice']));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Sign up to request clarification or add additional context in comments.

1 Comment

This worked, only thing is that I had to replace the Object.fromEntries to reduce method some thing like this (from node 8 :( ): r[key] = r[key] || { ...keys.map(k => [k, metric[k]]).reduce((o, [k, v]) => (o[k]=v, o), {}), values: [] };
1

Added explanation in comments.

const dummyData = [{ metric: { id: "A", code: "00", advice: "123" }, values: [[123, 1], [332, 2]] }, { metric: { id: "A", code: "01", advice: "123" }, values: [[123, 5], [332, 3]] }, { metric: { id: "B", code: "01", advice: "123" }, values: [[123, 3]] }];

// Param : array = array to be process
// spread list of props = pass properties on which you want to group.
function groupBy(array, ...props) {
  return array.reduce((a, c) => {
    // Find matching values exist for given properties.
    let obj = a.filter(x => props.every(y => x[y] == c.metric[y]))[0];
    // If not then create new object and push into result array.
    if (!obj) {
      obj = {};
      // Assign values for each properties.
      props.forEach(x => obj[x] = c.metric[x]);
      obj.values = [];
      a.push(obj);
    }

    // Loop through each values and find matching valueId. If exist add value else push value object.
    c.values.forEach(val => {
      let v = obj.values.filter(x => x.valueId == val[0])[0];
      if (!v) {
        obj.values.push({ valueId: val[0], value: val[1] });
      } else {
        v.value += val[1];
      }
    });
    
    return a;
  }, []);
}

console.log(groupBy(dummyData, 'id'));
console.log(groupBy(dummyData, 'id', 'code'));
console.log(groupBy(dummyData, 'id', 'advice'));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.