2

I'm really struggling with this. I'm creating a dot-plot in javascript using the d3 library. I would like to filter the dots actually being plotted so that later I can add text fields to some of them specified in a column in the dataset called 'highlight. Just as a test I'm only plotting the circles that are marked 'yes' but will eventually plot all circles. I've only included the actual plotting code as I'm pretty sure the data structure is OK. The following code plots the circles as I expect it to.

var category = plot.selectAll("."+media+"category")
        .data(plotData)
        .enter()
        .append("g")
        .attr("transform", function (d) {return "translate(0," + yScale(d.key) + ")"; })
        .attr("class", media+"category")
        .call(function(parent){
    parent.append('text')
        .attr("class", media+"Subtitle")
        .attr("x",margin.left)
        .attr("y",0)
        .text(function(d){return d.key})

    parent.selectAll('circles')
    .data(function(d){
        //console.log("object= ",d)
        let filtered=d.values.filter(function(d){
            return d.highlight=="yes"
        })
        //console.log("circles filtered ", filtered)
        return filtered
    })
    .enter()
    .append('circle')
    .attr("class",function(d,i){
        if(d.highlight=="yes"){
            return media+"highlight"
        }
        else {return media+"fill"}
    })
    .attr("id",function(d){return d.name +" "+d.value+ " "+d.size})
    .attr("cx",function(d){return xScale(d.value)})
    .attr("cy",yScale.rangeBand()*.4)
    .attr("r", function(d) {
        if (size) {return d.size*(yScale.rangeBand()*.003   )}
        else {return yOffset/2}
    })
    .attr("transform", function (d) {return "translate("+(margin.left)+","+(0)+")"})
    .style("fill",colours[0])

The plot looks like this:

enter image description here

The problem comes when I try to add the text. I'm using exactly the same filter to filter the data which creates an array for the .data aregument to use. However for some reason, although the first object IS in the array it is not plotted. My revised code looks like this:

var category = plot.selectAll("."+media+"category")
        .data(plotData)
        .enter()
        .append("g")
        .attr("transform", function (d) {return "translate(0," + yScale(d.key) + ")"; })
        .attr("class", media+"category")
        .call(function(parent){

        parent.append('text')
            .attr("class", media+"Subtitle")
            .attr("x",margin.left)
            .attr("y",0)
            .text(function(d){return d.key})

        parent.selectAll('circles')
        .data(function(d){
            //console.log("object= ",d)
            let filtered=d.values.filter(function(d){
                return d.highlight=="yes"
            })
            //console.log("circles filtered ", filtered)
            return filtered
        })
        .enter()
        .append('circle')
        .attr("class",function(d,i){
            if(d.highlight=="yes"){
                return media+"highlight"
            }
            else {return media+"fill"}
        })
        .attr("id",function(d){return d.name +" "+d.value+ " "+d.size})
        .attr("cx",function(d){return xScale(d.value)})
        .attr("cy",yScale.rangeBand()*.4)
        .attr("r", function(d) {
            if (size) {return d.size*(yScale.rangeBand()*.003   )}
            else {return yOffset/2}
        })
        .attr("transform", function (d) {return "translate("+(margin.left)+","+(0)+")"})
        .style("fill",colours[0])

        parent.selectAll('text')
        .data(function(d){
            console.log("object= ",d)
            let filtered=d.values.filter(function(d){
                return d.highlight=="yes"
            })
            console.log("text filtered ", filtered)
            return filtered
        })
        .enter()
        .append('text')
        .attr("x",function(d){
                    return xScale(d.value)+(margin.left);
                    })
                    .attr("y",function(d){
                    return yScale.rangeBand()*.4;
                    })
                    .text(function(d){
                        return d.name+' '+d.size
                    })
                    .attr("class",media+"subtitle")

but the plot looks like this:

enter image description here

No matter how many circles I define in the dataset as 'yes' so that they are plotted, the first circle is always missing its label. I'm guessing the data is getting mutated somewhere but can't figure out where. I've also tried plotting the text first before the circles just to see but the result is always the same.

This is the output from the console showing that the .data araay has the first items in it

[Object, Object, Object, Object]
dot-plot.js:104 object=  Object {key: "Endland", values: Array[22]}
dot-plot.js:108 text filtered  [Object, Object]0: Objectgroup: "Endland"highlight: "yes"name: "Manchester City"size: "95.65695168"value: "55097"__proto__: Object1: Objectgroup: "Endland"highlight: "yes"name: "Stoke"size: "9.13121722"value: "27743"__proto__: Objectlength: 2__proto__: Array[0]
dot-plot.js:104 object=  Object {key: "Spain", values: Array[44]}
dot-plot.js:108 text filtered  [Object, Object, Object]0: Objectgroup: "Spain"highlight: "yes"name: "Barcelona"size: "47.32724506"value: "100000"__proto__: Object1: Objectgroup: "Spain"highlight: "yes"name: "Deportivo de La Coru_a"size: "59.93202583"value: "34600"__proto__: Object2: Objectlength: 3__proto__: Array[0]
dot-plot.js:104 object=  Object {key: "Italy", values: Array[22]}
dot-plot.js:108 text filtered  [Object]
dot-plot.js:104 object=  Object {key: "Germany", values: Array[20]}
dot-plot.js:108 text filtered  [Object, Object]

I'd appreciate any insight that anyone may have. Really sorry for being so verbose but I've never used js.fiddle or I'd put an example up

Thanks

1 Answer 1

2

When you do this:

parent.append('text')
        .attr("class", media+"Subtitle")
        .attr("x",margin.left)
        .attr("y",0)
        .text(function(d){return d.key});

You are creating a text element in the SVG (in your case, the subtitle). So, when you do this later:

parent.selectAll('text')//here you select ALL text elements
    .data(function(d){
        console.log("object= ",d)
        let filtered=d.values.filter(function(d){
            return d.highlight=="yes"
        })
        console.log("text filtered ", filtered)
        return filtered
    })
    .enter()
    .append('text');

You are selecting that previous text (the subtitle). Thus, your enter selection has one element less.

Solution: select something else:

parent.selectAll('.somethingElse')//here you don't select existing elements
    .data(function(d){
        console.log("object= ",d)
        let filtered=d.values.filter(function(d){
            return d.highlight=="yes"
        })
        console.log("text filtered ", filtered)
        return filtered
    })
    .enter()
    .append('text') 
Sign up to request clarification or add additional context in comments.

1 Comment

I'd have never figured that out. Thanks very much working great now

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.