0

I am learning d3 in javascript and am working on some basic data manipulation stuff. I have a simple JSON object of year / month / births data that looks like this:

var birthData = [
  { 
    "year": 1967, 
    "month": "January", 
    "births": 31502 
  },
  { 
    "year": 1967, 
    "month": "January", 
    "births": 26703 
  },
  { 
    "year": 1967, 
    "month": "February", 
    "births": 28853 
  },
  { 
    "year": 1967, 
    "month": "February", 
    "births": 26958 
  },
  { 
    "year": 1967, 
    "month": "February", 
    "births": 28591 
  },
  { 
    "year": 1967, 
    "month": "March", 
    "births": 29545 
  },
  { 
    "year": 1967, 
    "month": "March", 
    "births": 30086 
  }]

and I would like to simply make an array of unique months that appear in this JSON object that looks like this:

["January", "February", "March"]

I would prefer to avoid using this approach

var months = [];
for(var i = 0; i <= birthData.length; i++) {
  var month = birthData[i].month
  if(months.indexOf(month) === -1) {
    months.push(month)
  }
}

Is there a more efficient way to do this?

EDIT: As a quick follow up question, are there any really good resources for learning data manipulation with javascript (particularly with d3 or other graphing libraries). Being very quick with data manip in javascript would make my life much easier!

Canovice
  • 9,012
  • 22
  • 93
  • 211
  • 2
    Possible duplicate of [What is the most efficient method to groupby on a JavaScript array of objects?](https://stackoverflow.com/questions/14446511/what-is-the-most-efficient-method-to-groupby-on-a-javascript-array-of-objects) – lin Mar 05 '18 at 00:19
  • D3 isn't necessary for this question. You can do it with Array methods built into JS. – keithlee96 Mar 05 '18 at 00:37

3 Answers3

2

Using reduce is a neat approach:

var months = birthData.reduce((accumulator, item) => 
  accumulator.includes(item.month) 
    ? accumulator 
    : [ ...accumulator, item.month ], []);

What this does is it loops through all items while building a new array (accumulator) and, with a condition that checks if a month has already been added to this new array, determines if it should add the month of the current item to the array or not.

The above code uses ES2015 syntax, here's the equivalent ES5 syntax:

var months = birthData.reduce(function(accumulator, item) { 
  return accumulator.indexOf(item.month) > -1
    ? accumulator 
    : accumulator.concat(item.month);
}, []);
Lennholm
  • 7,205
  • 1
  • 21
  • 30
2

You could map the months back to a new array, then use a Set, which only holds unique values, to make the array unique without iterating yourself

var months = [...new Set(birthData.map(i => i.month))]

var birthData = [{
    "year": 1967,
    "month": "January",
    "births": 31502
  },
  {
    "year": 1967,
    "month": "January",
    "births": 26703
  },
  {
    "year": 1967,
    "month": "February",
    "births": 28853
  },
  {
    "year": 1967,
    "month": "February",
    "births": 26958
  },
  {
    "year": 1967,
    "month": "February",
    "births": 28591
  },
  {
    "year": 1967,
    "month": "March",
    "births": 29545
  },
  {
    "year": 1967,
    "month": "March",
    "births": 30086
  }
]

var months = [...new Set(birthData.map(i => i.month))]

console.log(months)
adeneo
  • 312,895
  • 29
  • 395
  • 388
  • accepted this answer because i like the smart use of using a set since it only holds unique values, and its a 1-liner – Canovice Mar 06 '18 at 18:08
1
const months = birthData.map(month=>month.month);
const result = months.filter((item,pos)=>months.indexOf(item) === pos);
keithlee96
  • 1,724
  • 2
  • 11
  • 17