1

I have a meteorite landings dataset.
I nested the data in four keys, by type:

  var dataByType = d3.nest()
    .key(function(d) {
          return d.rectype;
    })
    .entries(dataset); // the original array

This is the resulting structure: data

enter image description here

Now I want to create new arrays, filtering the nested one: I want each filtered array to contain only the objects whose "mass" property is inside a defined range.
So I tried this:

// filter small meteorites
var lightWeight = dataByType.forEach(function(d){
      d.values.filter(function (object) {
            var mass = object.mass;
            return mass <= 100;
      });
  });

But it returns undefined.

Nesting is giving me a lot of trouble! What do I do wrong?
Thanks in advance

Russ Cam
  • 124,184
  • 33
  • 204
  • 266
Léonie
  • 37
  • 1
  • 8
  • Try to use `map` instead of `forEach` and return `d`. Like `var lightWeight = dataByType.map(function(d){ d.values.filter(function (object) { var mass = object.mass; return mass <= 100; }); return d; });` – Stan Liu Sep 11 '17 at 21:43
  • Please post your data as text not an image. – cнŝdk Sep 11 '17 at 22:01
  • Your d.values is an array so....d.values[1].filter? – Ben Sep 11 '17 at 22:05
  • @ben I tried `forEach(function(d, i){ d.values[i].filter(...)` but it returns an error saying "d.values[i].filter is not a function" – Léonie Sep 12 '17 at 16:53
  • @chsdk I'm sorry, I'm a newbie and can't figure out how to do that from the Firefox console – Léonie Sep 12 '17 at 16:55

4 Answers4

3

I think you're overwriting lightWeight on each pass of that forEach. Instead try creating lightWeight as its own object, then adding the keys you want in the loop:

const lightWeight = {};

dataByType.forEach(function(d){
  lightWeight[d.key] = d.values.filter(function (object) {
    var mass = object.mass;
    return mass <= 100;
  });
});

Working pen which (I think?) is doing what you want: https://codepen.io/benjaminwfox/pen/veBjrP?editors=0012

Ben
  • 5,079
  • 2
  • 20
  • 26
  • Many thanks, this does exactly what I need! So my mistake was not assigning the results to keys, inside the loop.. But then, why did I get undefined instead of an array with only the last result? Is it important to declare lightWeight as const instead of var? – Léonie Sep 13 '17 at 10:43
  • const vs var is syntactic difference between two versions of JavaScript (ES6 vs ES5). That's not actually relevant to the solution, it's just the syntax I've been using lately. I'll see if I can get an explanation for why it ended up undefined. – Ben Sep 13 '17 at 12:07
  • So the reason you were getting undefined is because you were trying to assign the return value of a forEach loop which, be design, returns undefined. See the MDN page: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach, and more info on the SO question: https://stackoverflow.com/questions/34653612/what-does-return-keyword-mean-inside-foreach-function Your .filter() function only returned the value of mass inside the scope of the forEach loop. – Ben Sep 13 '17 at 16:07
  • Thankyou very much for your patience! – Léonie Sep 13 '17 at 17:04
0

I couldn't speak for what your dataset looks like, but could it just be that you're not returning lightWeight at the end?

0

I am unsure what you are trying to filter on exactly, but try to use map instead of forEach and you need to return d in the iterators, anon function.

Like the following:

var lightWeight = dataByType.map(function(d) {
    d.filteredValues = d.values.filter(function (object) {
        return object.mass <= 100;
    });
    return d;
});
Stan Liu
  • 190
  • 2
  • 8
  • Actually `filter()` method will return a new `array` and does not change the array `in-place`, so this code won't be helpful. – cнŝdk Sep 11 '17 at 22:15
  • What if you stored it in another key within the object, like `filteredValues`? – Stan Liu Sep 11 '17 at 22:17
  • This will affect the initial array structure, but any way if you wanted so you could just do `d.values = d.values.filter(...)`. – cнŝdk Sep 11 '17 at 22:20
  • @StanleyLiu Yes this works, but changes the original array. In the end, though, I only need to group the data by 5 intervals of mass value, and this might do. I just hope it won't make accessing the data more difficult later. I'll try! Thanks – Léonie Sep 12 '17 at 17:06
0

Assuming that you got the data in the format you shared, where for each item of data you have a key property and a values array.

If you want to filter the values array by the mass property of each entry, you will need to use a combination of .map() and .filter() methods.

This is how should be your code:

var lightWeight = data.map(function(d){
    var obj = {};
    obj.key = d.key;
    obj.values = d.values.filter(function (object) {
            return object.mass <= 100;
    });
    return obj;
});

This will give you the expected results.

cнŝdk
  • 31,391
  • 7
  • 56
  • 78
  • I tried this, but still got an error saying "d.values is undefined" – Léonie Sep 12 '17 at 16:52
  • @Léonie normally this will work perfectly, can you please show us a Demo where you got this error, what data have you used? – cнŝdk Sep 13 '17 at 08:24