2

I have some dynamic data. Assume that id will always be present, and the other key:values will change.

I am trying to wrap my head around an abstract approach, but I don't think I know enough about the tools I have available.

I am looking for an approach to aggregate dynamically.

example, if I have this data:

const dataum = [{
  "id": 1,
  "q1": "yes",
  "q2": 4,
  "q3": null
}, {
  "id": 2,
  "q1": null,
  "q2": 14,
  "q3": "medium"
}, {
  "id": 3,
  "q1": "no",
  "q2": 83,
  "q3": "high"
}, {
  "id": 27,
  "q1": "yes",
  "q2": 62,
  "q3": null
}]

and am attempting to create something like this:

const aggData = [{
  "q1": {
    "yes": 2,
    "empty": 1,
    "no": 1
  }
}, {
  "q2": {
    "0to60": 2,
    "60to70": 1,
    "70to100": 1,
    "empty": 0
  }
} {
  "q3": {
    "medium": 1,
    "empty": 2,
    "high": 1
  }
}]

I haven't tried much. because I am stuck at the outer most process / function. I have thought about for() .keys and .values, I have looked at array.reduce()... but I still do not get how to be able to dynamically say: newObject.key.someAnswer += 1 where the key is dynamic, and the answer is dynamic (or null).

I wouldn't worry too much about the ranges... I think with a tiny bit of help I can fork the process.

I can provide some more data to this process, in any format, like:

const qProperties = [{
  "q1": {
    "type": "YN",
    "values": ["yes", "no"]
  }
}, {
  "q3": {
    "type": "LMH",
    "values": ["low", "medium", "high"]
  }
}]

If it would help reduce the code footprint, make it more abstract.

2 Answers 2

2

Although your input data does not really match your expected output data (which is probably why you got the downvote), I think you want something like that:

const dataum = [
  { id: 1, q1: "yes", q2: 4, q3: null },
  { id: 2, q1: null, q2: 14, q3: "medium" },
  { id: 3, q1: "no", q2: 83, q3: "high" },
  { id: 27, q1: "yes", q2: 62, q3: null },
];
const intermediate1 = dataum.reduce((acc, curr) => {
  Object.entries(curr).forEach(([k,v]) => {
    if (k !== "id") {
      if (!acc[k]) {
        acc[k] = [];
      }
      acc[k].push(v);
    }
  });
  return acc;
}, {});

console.log(intermediate1);
// {
//   q1: [ 'yes', null, 'no', 'yes' ],
//   q2: [ 4, 14, 83, 62 ],
//   q3: [ null, 'medium', 'high', null ]
// }

const final = Object.entries(intermediate1).reduce((acc, [k, v]) => {
  const accumulated = v.reduce((inner_acc, inner_val) => {
    if (inner_val === null) {
      inner_val = "empty";
    }
    if (!inner_acc[inner_val]) {
      inner_acc[inner_val] = 0;
    }
    inner_acc[inner_val] += 1;
    return inner_acc;
  }, {});    
  acc.push({
    [k]: accumulated,
  });
  return acc;
}, []);

console.log(final);      
// [
//   { q1: { yes: 2, empty: 1, no: 1 } },
//   { q2: { '4': 1, '14': 1, '62': 1, '83': 1 } },
//   { q3: { empty: 2, medium: 1, high: 1 } }
// ]

You may want to have a look into lodash, maybe they have something like that already.

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

2 Comments

this is so elegant thank you! Ive seen lodash come up a couple times. not sure what it does, just that in some cases people say 'use lodash'. Like.. moment does dates... what does lodash do?
look at the docs (lodash.com/docs/4.17.15) for detailed explanation and good examples. Lodash (successor of underscore) is a library for sorting / filtering / mapping / grouping / zipping / reducing / binning / finding / debouncing / checking / sequencing data for common use cases.
1

You could use lodash.groupby if you're not bothered about the implementation.

https://www.npmjs.com/package/lodash.groupby

const groupBy = require('lodash.groupby');

const keys = ["q1", "q2", "q2"];
const dataum = [
  { id: 1, q1: "yes", q2: 4, q3: null },
  { id: 2, q1: null, q2: 14, q3: "medium" },
  { id: 3, q1: "no", q2: 83, q3: "high" },
  { id: 27, q1: "yes", q2: 62, q3: null }
];

const grouped = [];

function countKeys(object) {
  const counts = [];
  const keys = Object.keys(object);

  keys.forEach((key) => {
    const count = Object.values(object[key]).length;
    counts.push({ [key]: count });
  });

  return counts;
}

keys.forEach((key) => {
  const groups = _.groupBy(dataum, key);
  const counts = countKeys(groups);
  const groupedRow = { [key]: counts };
  grouped.push(groupedRow);
});

console.log(grouped);

// const output = [
//   { q1: [{ yes: 2 }, { null: 1 }, { no: 1 }] },
//   { q2: [{ "4": 1 }, { "14": 1 }, { "62": 1 }, { "83": 1 }] },
//   { q2: [{ "4": 1 }, { "14": 1 }, { "62": 1 }, { "83": 1 }] }
// ];

3 Comments

I like using lodash, but Imho, this is not quite what OP wanted. For each qN he wanted to have a single object, and you have an array of objects. Also, OP wanted to have null replaced by "empty". Also I understood, that the keys (q1, q2, ...) are not known beforehand. Also where is your q3 with null/empty: 2?
@Joel yeah fair enough, was just an example, can be easily tweaked - the example in the OP is a bit confusing. Nice work on the answer anyway!
thanks @neeko array of objects works for me too. map(Object.values/keys into chartist). looks like i need to hit the books hard on lodash.

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.