1

I'm trying to use map.set(key, value) for a specific dataset - and assigning it to variable mapdata - instead of specifying it when I queue the csv.

I.e., instead of:

d3.queue()
  .defer(d3.json, 'counties.json')
  .defer(d3.csv, 'employmentdata.csv') function(d) {
      d3.map()
      .set(d.county, +d.rate);
   })
  .await(ready);

I want something more along the lines of

var mapdata = d3.map(data)
  .set(function(d) {d.county}, function(d) {d.rate});

except the code isn't returning what I want. the indexes are keys instead of the counties, and the values include the entire dataset instead of just rate. How would I fix this?

Thanks in advance

4
  • can you give example data? Commented Apr 18, 2018 at 21:34
  • id,county,rate,occ,education 01,place1,80,'Business & Administration",Bachelor's Degree,0 02,place2,15,'applied sciences',Certificate/Diploma,12 Commented Apr 18, 2018 at 21:48
  • @TobiasK the format of the data isn't the problem here (its worked with other things), I think I'm using the d3.map() function incorrectly Commented Apr 18, 2018 at 21:49
  • Here is a little exmaple of d3.map() : var map = d3.map([{name: "foo"}, {name: "bar"}], function(d) { return d.name; }); map.get("foo"); // {"name": "foo"} map.get("bar"); // {"name": "bar"} map.get("baz"); // undefined Commented Apr 18, 2018 at 22:00

3 Answers 3

5

When used as such:

.defer(d3.csv, 'employmentdata.csv') function(d) {
      map
      .set(d.county, +d.rate);
   })

The callback function with map.set is called for each row in the csv, and each key and value is set individually. If you have an array of data already, you can emulate this with a forEach loop:

var data = [
  {key: "A", value: 100},
  {key: "B", value: 200},
  {key: "C", value: 300},
  {key: "D", value: 400},
  {key: "E", value: 500}
]

var map = d3.map();
data.forEach(function(d) {
  map.set(d.key, d.value);
})

console.log(map.get("A"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

We can expand this to manipulate the key and value, as the row function or a forEach loop allows access to the datum d, we don't need to actually enclose the manipulation in a function:

var data = [
  {key: "A", value: 100},
  {key: "B", value: 200},
  {key: "C", value: 300},
  {key: "D", value: 400},
  {key: "E", value: 500}
]

var map = d3.map();
data.forEach(function(d,i) {
  map.set(d.key+"-"+i, d.value*2);
})

console.log(map.get("A-0"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>


Your second example:

var mapdata = d3.map(data)
  .set(function(d) {d.county}, function(d) {d.rate});

This will not work because .set is only being called once, and d is not defined. In fact, the functions will not be called at all.

Before we look at the functions in the set method, let's look at the first line a little closer:

   var mapdata = d3.map(data)

d3.map expects two parameters, one defining the data array, and one defining the key. As you don't provide a key, the default key is used, index:

d3.map([object[, key]]) <>

Constructs a new map. If object is specified, copies all enumerable properties from the specified object into this map. The specified object may also be an array or another map. An optional key function may be specified to compute the key for each value in the array. (source)

So, the behavior you are seeing doesn't come from the .set method but simply from the default indexing:

var data = [
  {key: "A", value: 100},
  {key: "B", value: 200},
  {key: "C", value: 300},
  {key: "D", value: 400},
  {key: "E", value: 500}
]

var map = d3.map(data);

console.log(map.get(0));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

So what is .set doing in your example? It is adding a new value key pair to your dataset, let's take a close look at the map before and after:

var data = [
  {key: "A", value: 100},
  {key: "B", value: 200},
  {key: "C", value: 300},
  {key: "D", value: 400},
  {key: "E", value: 500}
]

var map = d3.map(data);

console.log(map);
console.log("---------------");

map.set(function(d) { console.log(d); },
        function(d) { console.log(d); })
        
console.log(map);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

This is interesting behavior, not only are the console.logs not called in the set method, the two functions are added as key, value pairs in the array. It's easy to miss (largely due to stack snippet's logging), but here's the addition to the map:

"$function (d) { console.log(d); }": function (d) { console.log(d); },

The set method will only add one key value pair, it will not modify existing pairs (unless to overwrite one). This is why .set will work in a row function or a forEach loop, but not otherwise when setting multiple key/value pairs.

While the function that was key was coerced to a string, you could embed functions as values:

var data = [
  {key: "A", value: 100},
]

var map = d3.map(data);

map.set("B",function() { return "Hello"; })
        
console.log(map.get("B")());
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>


Summing Up

So, if you want to create a map using a key and the whole object as a value you can use:

var data = [
  {key: "A", value: 100},
  {key: "B", value: 200},
  {key: "C", value: 300},
  {key: "D", value: 400},
  {key: "E", value: 500}
]

var map = d3.map(data, function(d) { return d.key; });

console.log(map.get("A"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

But if you want to specify a certain value for each key (as opposed to the data array item), it will be easier to use a forEach loop (as seen in the first snippet in this answer):

var map = d3.map()
data.forEach(function(d) {
  map.set(d.key,d.value)
})

If you want to use a function in the set method here, you would need to execute the function and have a return value (both missing in your second example/desired code), you want the function's outputs, not the function itself:

var data = [
  {key: "A", value: 100},
  {key: "B", value: 200},
  {key: "C", value: 300},
  {key: "D", value: 400},
  {key: "E", value: 500}
]

var map = d3.map();

data.forEach(function(d) {
  map.set(function() { return d.key + "-key"}(),
          double(d.value))
})

console.log(map.get("A-key"));

function double(n) { return n*2; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>


A last note, your initial (reference/starting point) code block isn't quite set up properly, d3.map() creates a new map. We don't want to create a new map for each row, we want to create a map once and then use set for each row.

Consequently this:

d3.queue()
  .defer(d3.json, 'counties.json')
  .defer(d3.csv, 'employmentdata.csv') function(d) {
      d3.map()
      .set(d.county, +d.rate);
   })
  .await(ready);

Should read like:

var map = d3.map();  // create a map.
d3.queue()
  .defer(d3.json, 'counties.json')
  .defer(d3.csv, 'employmentdata.csv') function(d) {
      map // use that same map for each row.
      .set(d.county, +d.rate);
   })
  .await(ready);

The snippets above use this same approach, create a map, and call .set for each item in the array.

Conversely, something like : d3.map().get(d.property) won't return anything because you've just created a new map which is empty with d3.map().

Sign up to request clarification or add additional context in comments.

Comments

0

Thank you for your explanation. I tried your above code and it returns undefined. I might know what the issue is:

So my current data has multiple categories. I want to create different keys and values for different categories, and I want to distinguish them by assigning them to new variables. I want to do this because I want to add different features on my map for different data, for instance, i want to color the map based on unemployment, but I want to add bubbles on the map based on a different rate, and then I want to create dropdown menus based on something else. For instance, I currently have:

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

   // array of locations in the data
   var nocs = nest.map(function(d) { return d.key; });

So, when I'm creating my dropdown menu, I write this:

var nocMenu = d3.select('#dropDown')
    nocMenu
      .append("select")
      .attr("id", "selectMenu")
      .selectAll("option")
        .data(nest)   /// i'm using 'nest' now instead of the entire dataset
        .enter()
        .append("option")
        //.attr("value", function(d) { return d.values })
        .text(function(d) { return d.key; });

And this gives me what I want. The only relationship between the topojson file and the csv file is the county names. I need to have county names as a key with unemployment as the value so I can use d3.map().get(d.properties.counties) when filling my map with color.

I tried:

  var mMap = d3.map()
var mapdata = data.forEach(function(d) {
  mMap
    .set(d.geography, +d.rate)
});

console.log(mapdata);

but it returned undefined.

3 Comments

mapdata isn't defined because a forEach loop isn't returning anything, your map is mMap, console.log(mMap) should return something. mMap.get("appropriate key") will return something as well. You can't use d3.map().get() as d3.map() creates a new map, from which you can't get anything before adding things to it. I'll also caution that continuations to questions posted as answers tend to be deleted and/or downvoted as non-answers.
Great - I noticed the use of d3.map().set(d.county, +d.rate); in the question, but thought it might be a transcription error, I'll update my answer to reflect this.
Please use the Post answer button only for actual answers. You should modify your original question to add additional information, or add a comment to the answer to provide any updates. Otherwise, the post may be deleted.
0

As D3 version 6, v6.4.0

d3.map() with not parameters give me error: values is undefined. When degrade to version v5 the error also comes up until changed to v4

The following is my code show the error:

<!DOCTYPE html>
<html>
<head>
  <title>D3 Example</title>

  <!-- 
      All of this gives error: values is undefined:

  <script src="https://d3js.org/d3.v5.min.js"></script>
  <script src="node_modules/d3/dist/d3.js"></script>
  <script src="unziped/d3.js"></script>
  -->

  <script src="https://d3js.org/d3.v6.min.js"></script>

  <!--
      Version 4, This is ok:
  <script src="https://d3js.org/d3.v4.js"></script>
  -->

  <script>
  var data_map = d3.map();
  </script>
</head>
<body>
    <h1>Try D3, d3.map() gives error</h1>
    <div id="map"></div>
</body>
</html>

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.