Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I have two javascript functions that generate a D3 Stream graph. I feel I may be excessively and unnecessarily iterating over my dataset to get it into the desired form.

How might I optimize or simplify this?

     d3.json("/sleeps", function(d) {
            sleepStream(d);
        });

        function sleepStream(d) {

            var divheight   = $(document).height() 
              , divwidth    = $(document).width()
              , margin      = { top: 20 , right: 20 , bottom: 20 , left: 10 }
              , width       = divwidth - margin.left - margin.right
              , height      = (divheight / 8) - margin.top - margin.bottom;

            var colorrange  = ["#80B2B7", "#4B98B1", "#1C7BAC", "#235AA0", "#453485"]
              , datearray   = []
              , strokecolor = '#FFF';

            var x = d3.time.scale().range([0, width])
              , y = d3.scale.linear().range([height, 0])
              , z = d3.scale.ordinal().range(colorrange);

            var xAxis = d3.svg.axis()
                .scale(x)
                .orient("bottom")
                .ticks(d3.time.months);

            var yAxis = d3.svg.axis().scale(y);

            var nest = d3.nest()
                .key(function (d) { return d.key; });

            /*  
             * Helper function to extract (subset) Jawbone results. 
             * Iterates through data and returns desired groups and
             * values.
             */
            function getData(d) {
                var _data = [];
                var parseDate = d3.time.format("%Y%m%d").parse;

                for (var i = 0; i < d.length; i++) {

                    var _date = parseDate(d[i].date.toString());

                    _data[i] = {
                        'date' : _date 
                        , 'light': Math.round(d[i].details.light / 60)
                        // Make it a calculated value due to some dirty data
                        // that screws up path vals
                        , 'deep' : Math.round((d[i].details.duration - (d[i].details.light + d[i].details.awake)) /60)
                        , 'awake': Math.round(d[i].details.awake / 60)
                        , 'duration': Math.round(d[i].details.duration / 60)
                    };
                };
                return _data;
            };

            var tmpdata = getData(d);
            var data = [];

            /* 
             * RFORMAT tmpdata JSON FOR D3 STREAM 
             */
            tmpdata.forEach(function (e) {
                for (var key in e) {
                    if (key !== 'date')
                        data.push({
                            key: key
                            , date: e.date
                            , value: e[key]
                        });
                }
            });

            /*
             * Pad missing values
             */
            assignDefaultValues(data); //data.forEach(function(d){ console.log(d); });

            var stack = d3.layout.stack()
                .offset("silhouette")
                .values(function (d) { return d.values; })
                .x(function (d) { return d.date; })
                .y(function (d) { return d.value; });

            var layers = stack(nest.entries(data));

            var area = d3.svg.area()
                .interpolate("basis")
                .x(function (d) { return x(d.date); })
                .y0(function (d) { return y(d.y0); })
                .y1(function (d) { return y(d.y0 + d.y); });

            var svg = d3.select("#sleep-stream-chart").append("svg")
                .attr("width", width - margin.left - margin.right)
                .attr("height", height + margin.top + margin.bottom)
                .append("g")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

                x.domain(d3.extent(data, function (d) { return d.date; }));

                y.domain([0, d3.max(data, function (d) { return d.y0 + d.y; })]);

                svg.selectAll(".layer")
                    .data(layers)
                    .enter().append("path")
                    .attr("class", "layer")
                    .attr("d", function (d) { return area(d.values); })
                    .attr("transform", "translate(8, 0)")
                    .style("fill", function (d, i) { return z(i); });

                svg.append("g")
                    .attr("class", "x axis")
                    .attr("transform", "translate(8," + height + ")")
                    .attr("class", "axis")
                    .attr("fill", "#fff")
                    .call(xAxis);

            var tooltip = d3.select("#sleep-stream-chart")
                    .append("div")
                    .attr("class", "remove")
                    .style("position", "absolute")
                    .style("z-index", "20")
                    .style("visibility", "hidden")
                    .style("top", (height / 2) + "px")
                    .style("left", (width / 3) + "px");

            svg.selectAll(".layer")
                .attr("opacity", .5)
                .on("mouseover", function (d, i) {
                    svg.selectAll(".layer").transition()
                        .duration(250)
                        .attr("opacity", function (d, j) {
                            return j != i ? 0.5 : 1;
                        })
                })
                .on("mousemove", function (d, i) {
                    mousex = d3.mouse(this);
                    mousex = mousex[0];
                    var invertedx = x.invert(mousex);
                    invertedx = invertedx.getMonth() + invertedx.getDate(); 
                    var selected = (d.values);

                    for (var k = 0; k < selected.length; k++) {
                        datearray[k] = selected[k].date;
                        datearray[k] = datearray[k].getMonth() + datearray[k].getDate();
                    }

                    _mousedate = datearray.indexOf(invertedx);
                    _tmpdate = x.invert(mousex);

                    pro = d.values[_mousedate].value;

                    d3.select(this)
                        .classed("hover", true)
                        .attr("stroke", strokecolor)
                        .attr("stroke-width", "0.5px");

                    tooltip.html("<p>" + _tmpdate + " " + pro + " " + d.key +"</p>")
                        .style("visibility", "visible")
                        .style("color", "white");

                })

                .on("mouseout", function (d, i) {
                    svg.selectAll(".layer")
                        .transition()
                        .duration(250)
                        .attr("opacity", "1");

                    d3.select(this)
                        .classed("hover", false)
                        .attr("stroke-width", "0px")

                    tooltip.html("<p>" + _tmpdate + " " + pro + " " +  d.key +"</p>")
                        .style("visibility", "hidden");
                })

            /*
             * A vertical white bar that tracks horizontal mouse movement.
             * It aids visual inspection if substreams by aligning a point
             * with the x axis.
             */
            var vertical = d3.select("#sleep-stream-chart")
                .append("div")
                .attr("class", "remove")
                .style("position", "absolute")
                .style("z-index", "19")
                .style("width", "1px")
                .style("height", divheight)
                .style("top", "10px")
                .style("bottom", "0px")
                .style("left", "0px")
                .style("background", "#fff");

            d3.select("#sleep-stream-chart")
                .on("mousemove", function () {
                    mousex = d3.mouse(this);
                    vertical.style("left", (mousex[0]) + "px")
                })
                .on("mouseover", function () {
                    mousex = d3.mouse(this);
                    vertical.style("left", (mousex[0]) + "px")
                });

            /*
             * Turn off the Loading Spinner
             */
            spinner4.stop();
        }; 

        function assignDefaultValues(dataset) {
            defaultValue = 0;
            keys = ['Group1', 'Group2', 'Group3', 'Group4'];
            hadData = [true, true, true, true];
            newData = [];
            previousdate = new Date();
            sortByDate = function (a, b) {
                return a.date > b.date ? 1 : -1;
            };

            dataset.sort(sortByDate);
            dataset.forEach(function (row) {
                if (row.date.valueOf() !== previousdate.valueOf()) {
                    for (var i = 0; i < keys.length; ++i) {
                        if (hadData[i] === false) {
                            newData.push({
                                key: keys[i]
                                , value: defaultValue
                                , date: previousdate
                            });
                        }
                        hadData[i] = false;
                    }
                    previousdate = row.date;
                }
                hadData[keys.indexOf(row.key)] = true;
            });
            for (i = 0; i < keys.length; ++i) {
                if (hadData[i] === false) {
                    newData.push({
                        key: keys[i]
                        , value: defaultValue
                        , date: previousdate
                    });
                }
            }
            return dataset.concat(newData).sort(sortByDate);
        }
share|improve this question

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.