Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

finding d3 extent of multiple values in object array

My data array:

var data = [{glazed: 3.50, jelly: 4.50, powdered: 1.00, sprinkles: 3.50, age: 21, responses: 2,name:"test"},
    {glazed: 2.83, jelly: 3.50, powdered: 1.83, sprinkles: 4.50, age: 22, responses: 6,name:"test"},
    {glazed: 3.25, jelly: 4.75, powdered: 2.25, sprinkles: 3.50, age: 23, responses: 4,name:"test"},
    {glazed: 1.50, jelly: 4.00, powdered: 2.50, sprinkles: 4.00, age: 25, responses: 2,name:"test"}];

If I wanted to find extent of either glazed or jelly or powdered or sprinkles to use for scaling, I would use a code as below..

var x = d3.scale.linear()
                .domain(d3.extent(data, function (d) {
                    return d.glazed;//or jelly etc..
                }))
                .range([0, width]);

What would I need to do to get the extent of all values in glazed, jelly, powdered and sprinkles rather all values which are not age, responses and name.

This is because json file gets created dynamically and so I will have no idea of the key values except age, responses and name.

So, my requirement is that it should give me min as 1.5 (from glazed) and max as 4.75 (from jelly)

Any help is sincerely appreciated..

Thanks

like image 893
Arnab Avatar asked Apr 07 '15 14:04

Arnab


2 Answers

Assuming that you want to be able to handle any key values except age, responses and name, and that others in addition to glazed, jellied, powdered & sprinkled might appear, this approach should work to calculate max and min values:

var keys_to_ignore = ["age", "responses", "name"]

data.forEach( function (row)
{
    //Use d3.entries to create key\values
    var data_entries = d3.entries(row)
    // Add the 'current maximum' and 'current_min' based off the previous rows
    // If this is the first row, the extent will be undefined
    if (typeof extent !== "undefined") {
        data_entries.push({"key":"current_max", "value":extent[1]})
        data_entries.push({"key":"current_min", "value":extent[0]})
    }
    // now calculate the extent (max / min)
    extent = d3.extent(data_entries, function (d) {
        // Ignore specified keys
        if (keys_to_ignore.indexOf(d.key) == -1) {
            return d.value;
        }
    });
});

console.log(extent)

Using d3.entries will create objects with a key and value attribute, making the data easier to manage.

Working fiddle here: http://jsfiddle.net/henbox/aa4d0z81/

I feel like there's a more elegant way to do this, but I haven't managed to figure it out

like image 35
Henry S Avatar answered Nov 14 '22 23:11

Henry S


var x = d3.scale.linear()
   .domain(d3.extent(data.map(function (item) {
        return (item.glazed);
   })))
   .range([0, width]);

map() returns [3.5, 2.83, 3.25, 1.5]

extent() returns [1.5, 3.5]

In case you need absolute minimum and maximum for all properties of the data, you should concatenate arrays:

var x = d3.scale.linear()
   .domain(d3.extent(
    [].concat(data.map(function (item) {
        return (item.glazed);
    }), data.map(function (item) {
        return (item.jelly);
    }), data.map(function (item) {
        return (item.powdered);
    }), data.map(function (item) {
        return (item.sprinkles);
    }))))
   .range([0, width]);

Finally, if you have a list of valuable properties, you should replace [].concat(...) expression by anonymous function and call it immediately like this: function(array, names){...}(data, temp). You should know, in JavaScript array.property and array["property"] -- are the same calls.

var temp = ["glazed", "jelly", "powdered", "sprinkles"];
var width = 1000;
var data = [{glazed: 3.50, jelly: 4.50, powdered: 1.00, sprinkles: 3.50, age: 21, responses: 2,name:"test"},
    {glazed: 2.83, jelly: 3.50, powdered: 1.83, sprinkles: 4.50, age: 22, responses: 6,name:"test"},
    {glazed: 3.25, jelly: 4.75, powdered: 2.25, sprinkles: 3.50, age: 23, responses: 4,name:"test"},
    {glazed: 1.50, jelly: 4.00, powdered: 2.50, sprinkles: 4.00, age: 25, responses: 2,name:"test"}];


var x = d3.scale.linear()
   .domain(d3.extent(
       function(array, names){
          var res = [];
          array.forEach(function(item){
             names.forEach(function(name){
                res = res.concat(item[name]);
             });
          });
          return(res);
       }(data, temp)
    ))
   .range([0, width]);

DEMO: http://jsfiddle.net/v5qzuuhj/

like image 67
Ruben Kazumov Avatar answered Nov 14 '22 22:11

Ruben Kazumov