Visualize Data with a Choropleth Map

Tell us what’s happening:
I am having trouble matching the two data sets, in order to add accurate tooltips to my map.

I tried to loop over the data, but that breaks my map. I think the dataset is too large to use a for loop effectively.

Here is my project. I am trying to add .text at line 41: https://codepen.io/jennnico/pen/GXjaem?editors=1010

Your code so far

 svg.append("g")
    .attr("class", "counties")
    .selectAll("path")
    .data(topojson.feature(us, us.objects.counties).features)
    .enter().append("path")
    .attr("d", path)
    .append("title")
 //NOTE: d.id === fips. Need to make a function to match these. d.id is NOT in ascending order.
 //Notice that the numbers do not match.
    //.text((d, i) => (d.id + ' and ' + json[i].fips))
//For loop attempt, which broke everything:
    // .text((d, i) => (
    //   for(var k=0; k<json.length; k++){
    //       d.id === json[k].fips ? return json[k].area_name : return "odd";
    //       }
    //    )

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36.

Link to the challenge:

Hi,
I’ve just completed Choropleth Map myself so I thought it would be a good time to help someone else to do the same :slight_smile:

Your code:

.text((d, i) => (
  for(var k=0; k<json.length; k++){
    d.id === json[k].fips ? return json[k].area_name : return "odd";
  }
)
  1. line 1: you don’t need i as you aren’t using it in your code. Technically it isn’t breaking your code but it is redundant. Fix: remove i.
  2. line 1: you have an opening parenthesis after =>. And no closing parenthesis by the way - but it’s not important in this case as you should’t use parenthesis here at all. Rather you should be using curly braces when writing arrow functions. As you have more than one line in your arrow function body you need braces around to signify a new block that allows for multiple statements. Without braces, the arrow function can only execute one line. Fix: remove opening parenthesis and surround block with curly braces.
  3. line 3: the syntax of your ternary operator is incorrect. It should look like this condition ? expression1 : expression2 and in your code it looks like this condition ? statement1: statement2. The conditional operator requires expressions, not statements. return is a statement. Fix: remove both return statements from your ternary operator and add return statement in front of ternary operator like so: return condition ? expression1 : expression2.
  4. Now let’s look together what our corrected ternary operator is returning:
.text(d => {
  for(var k = 0; k < json.length; k++) {
    return d.id === json[k].fips ? json[k].area_name : "odd";
  }
})

Setting text for tooltip of first county:
first loop iteration:

  • sets k to 0
  • checks if d.id === json[0].fips
  • returns "odd" as d.id === json[0].fips evaluates to false

It will NOT continue to loop because of the return statement. The return statement stops the execution and returns a value.

The same goes for other counties too - all counties but one will get tooltip with text “odd”.
And only one county will get desired tooltip (as only one time d.id === json[0].fips will evaluate to true).

image

At this point you can hover over your map and see the tolltips. All display text “odd” and only one displays correctly “Autauga County”.

  1. The problem (as we already determined) is return statement which breaks us out of the loop prematurely. Rewriting our ternary operator to an if statement would help:
.text(d => {
  for(var k = 0; k < json.length; k++) {
    if (d.id === json[k].fips) {
      return json[k].area_name;
    }
  }
  return "odd";
})

Setting text for tooltip of first county:
first iteration:

  • sets k to 0
  • checks if d.id === json[0].fips which evaluates to false
  • loop goes to next iterations:
    • until d.id === json[k].fips evaluates to true (return json[k].area_name)
    • or loop has gone through all education file and didn’t found matching id (return "odd").
  1. Alternative solution - you could use filter function instead of for loop as it might be easier to read:
.text(d => {
  const result = json.filter(obj => d.id === obj.fips);
  return result[0] ? result[0].area_name : "odd";
})

First you filter out json array. Now result variable stores either an empty array (if filter function couldn’t find matching ids) or an array with one object (if filter function did find matching ids).
Next I am using ternary operator to check if result is an empty array (result[0] evaluates to false) or not (result[0] evaluates to true).

I wrote a lot :slight_smile: so if you find something confusing - ask away :wink:

Thank you for this thorough explanation! Everything is very clear and now I can move forward on my project.

1 Like