1

I want to use the Charts.js Library and make the data displayed in the graph dependent on what a user has entered.

Right now I am not quite sure how to get the data in the right format so that I can use if for the library.

Currently I have a problem when combining the labels and values so that it matches the right index for the right label. I am having a table with two values ( A Month (Specified as Jan,Feb,Mar,Apr.etc.) and a numeric Value)

Now I need to combine months that appear twice and add the values associated with each month. I dont really know if I should use a 2d Array or 2 arrays for this. For example

var labels = ["Jan 17", "Jan 17", "Feb 17", "Mar 17", "Mar 17"];
var values = [10,10,25,40,100];

becomes

var newlabels = ["Jan 17", "Feb 17", "Mar 17"];
var newvalues = [20,25,140];

or as 2D Array:

var graphdata = [["Jan 17", 10],["Jan 17",10],["Feb 17",25],["Mar 17",40],["Mar 17",100]];

becomes

var newgraphData =[["Jan 17", 20],["Feb 17",25],["Mar 17",140]];

Either way is fine but I dont know which to use. Also I wouldn't quite know how to get the data in a 2d Array with jQuery selectors and the .map() function.

I was thinking of using the .map() and .reduce() function with a loop to go through each index but I cant quite seem to figure out how to do it.

2
  • Why not [{ 'label': 'Jan 17', val: '100'}, ....]? Commented Jan 27, 2018 at 18:33
  • Well yea that could work, but how would I combine the duplicate labels and add the associated numeric values? Commented Jan 27, 2018 at 18:37

3 Answers 3

1

Try this. You will get the formatted data in the arrays rLabels and rValues.

var labels = ["Jan 17", "Jan 17", "Feb 17", "Mar 17", "Mar 17"]
  , values = [10, 10, 25, 40, 100]
  , rLabels = []
  , rValues = [];
for (var i = 0; i < labels.length; i++) {
    var label = labels[i];
    var ind = rLabels.indexOf(label);
    if (ind == -1) {
        rLabels.push(label);
        rValues.push(values[i]);
    } else {
        rValues[ind] += values[i];
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Array.prototype.indexOf consumes linear time – grouping with Map can be done in logarithmic time.
Thank this works totally fine. @naomik how would you use map there?
1
const labels = ["Jan 17", "Jan 17", "Feb 17", "Mar 17", "Mar 17"];
const values = [10,10,25,40,100];

const result = labels.map((label, i) => ({label, value: values[i]}));
const grouped = []

result.forEach(o => {
  if (!this[o.label]) {
    this[o.label] = { label: o.label, value: 0 };
    grouped.push(this[o.label]);
  }
  this[o.label].value += o.value;
}, {});
console.log(grouped) // [ { label: 'Jan 17', value: 20 }, { label: 'Feb 17', value: 25 }, { label: 'Mar 17', value: 140 } ]

Comments

1

Here's another approach that aims to deconstruct the problem into smaller parts. Once we have a couple functions to transform data in a high-level way, assembling the final result becomes a trivial task.

We can start with a way to combine your two lists in a zipper-like fashion, using zip

const None =
  Symbol ()

const zip = ([ x = None, ...xs ], [ y = None, ...ys ]) =>
  x === None || y === None
    ? []
    : [ [ x, y ] ] .concat (zip (xs, ys))

const labels =
  [ "Jan 17", "Jan 17", "Feb 17", "Mar 17", "Mar 17" ]

const values =
  [ 10, 10, 25, 40, 100 ]

console.log (zip (labels, values))
// [ [ 'Jan 17', 10 ]
// , [ 'Jan 17', 10 ]
// , [ 'Feb 17', 25 ]
// , [ 'Mar 17', 40 ]
// , [ 'Mar 17', 100 ] 
// ]

With a list of [ key, value ] pairs, we can create a Map that groups values by key. Maps are lightning fast thanks to logarithmic search. JavaScript gives us Map, so when applicable, it's best to use it instead of a plain Object. At the very least, avoid using Array.prototype.indexOf like one answer suggests as it uses linear search and not suitable for larger data sets.

const group = (xs = []) =>
  xs.reduce ((acc, [ key, value ]) =>
    acc.has (key)
      ? acc.set (key, value + acc.get (key))
      : acc.set (key, value),
    new Map ())

console.log (group (zip (labels, values)))
// => Map { 'Jan 17' => 20, 'Feb 17' => 25, 'Mar 17' => 140 }

Lastly, we can convert from a Map to your desired output type using Array.from, Map.prototype.entries

const main = (labels = [], values = []) =>
  Array.from (group (zip (labels, values)) .entries ())

Here's a functioning example

const None =
  Symbol ()

const zip = ([ x = None, ...xs ], [ y = None, ...ys ]) =>
  x === None || y === None
    ? []
    : [ [ x, y ] ] .concat (zip (xs, ys))
  
const group = (xs = []) =>
  xs.reduce ((acc, [ key, value ]) =>
    acc.has (key)
      ? acc.set (key, value + acc.get (key))
      : acc.set (key, value),
    new Map ())

const main = (labels = [], values = []) =>
  Array.from (group (zip (labels, values)) .entries ())

const labels =
  [ "Jan 17", "Jan 17", "Feb 17", "Mar 17", "Mar 17" ]
  
const values =
  [ 10, 10, 25, 40, 100 ]

console.log (main (labels, values))
// [ [ 'Jan 17', 20 ]
// , [ 'Feb 17', 25 ]
// , [ 'Mar 17', 140 ]
// ]

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.