13

I have a data structure that look like this

const arr = [{
    name: "Alice",
    created_at : "2017-04-18"
},
{
    name: "James",
    created_at : "2017-06-30"
},
{
    name: "Melisa",
    created_at : "2017-04-03"
},
{
    name: "Amy",
    created_at : "2017-05-03"
}];

I want to restructure it so that I can display them. I want to display them by month, like so

April - Alice - Melisa

May - Amy

June - James

I have no clue where to begin.

6 Answers 6

28

Here's a solution that uses moment and lodash.

First we create a helper function that uses moment to create a date and then then extract the shortened month name.

// helper function to get the month name from an item
const monthName = item => moment(item.created_at, 'YYYY-MM-DD').format('MMM');

Now use lodash to group by date and then map across each group to get the names for each group. The following snippet uses implicit chaining:

// group items by month name and then get the name for each month
const result = _(arr)
    .groupBy(monthName)
    .mapValues(items => _.map(items, 'name'))
    .value()

As above but with explicit chaining:

// group items by month name and then get the name for each month
const result = _.chain(arr)
    .groupBy(monthName)
    .mapValues(items => _.map(items, 'name'))
    .value()

UPDATE

To get all the items for each month then it simplifies to just this:

const result = _.groupBy(arr, monthName);

result will now look like this:

{
    Apr: [
        { name: "Alice", created_at : "2017-04-18" },
        { name: "Melisa", created_at : "2017-04-03" }
    ],
    Jun: .....
}
Sign up to request clarification or add additional context in comments.

10 Comments

wow the first line blow my time, I didn't know function can be 1 line just like that.
where does _(arr) come from?
what if I have multiple property besides names? how the mapValues callback look like?
Learned something today, thank you for your clear explanation Gruff!
To group by year and month change the format string to 'YYYY-MMM'. The keys in the result object will then look like: '2017-Apr' etc. I'm afraid I don't know react so it may be worth posting a second question
|
2

We can use moment#format to get the name of the month and use this with lodash#groupBy to group the items by months. Lastly, use lodash#mapValues and lodash#map to transform each item by name.

var result = _(arr)
  .groupBy(v => moment(v.created_at).format('MMMM'))
  .mapValues(v => _.map(v, 'name'))
  .value();

const arr = [{
    name: "Alice",
    created_at : "2017-04-18"
},
{
    name: "James",
    created_at : "2017-06-30"
},
{
    name: "Melisa",
    created_at : "2017-04-03"
},
{
    name: "Amy",
    created_at : "2017-05-03"
}];

var result = _(arr)
  .groupBy(v => moment(v.created_at).format('MMMM'))
  .mapValues(v => _.map(v, 'name'))
  .value();
  
console.log(result);
body > div { min-height: 100%; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

2 Comments

what if I have multiple property besides names? how the mapValues callback look like?
@ryeballar what if have multiple stuff apart from names and just want to get back the full data but just grouped by the month.
1

You can group them using key-value pairs like this using plain javascript. You will also get it in sorted order automatically

const arr = [{ name: "Alice", created_at : "2017-04-18" }, { name: "James", created_at : "2017-06-30" }, { name: "Melisa", created_at : "2017-04-03" }, { name: "Amy", created_at : "2017-05-03" }];

var m =['','January','February','March','April','May','June','July','August','September','October','November','December'];

var result={}

// restructuring loop
for(var i=0;i<arr.length;i++){
  var month = parseInt(arr[i].created_at.substring(5,7))
  if(!result[month]){
    result[month] = [arr[i].name]
  }
  else{
    result[month].push(arr[i].name)
  }
}

//display loop
for (var k in result){
    if (result.hasOwnProperty(k)) {
         console.log(m[k]+'-->'+result[k])
    }
}

Comments

1

Try This

var res = [
    {month:"January",elems:[]},
    {month:"February",elems:[]},
    {month:"March",elems:[]},
    {month:"April",elems:[]},
    {month:"May",elems:[]},
    {month:"June",elems:[]},
    {month:"July",elems:[]},
    {month:"August",elems:[]},
    {month:"September",elems:[]},
    {month:"October",elems:[]},
    {month:"November",elems:[]},
    {month:"December",elems:[]}
];

const arr = [{
        name: "Alice",
        created_at: "2017-04-18"
    },
    {
        name: "James",
        created_at: "2017-06-30"
    },
    {
        name: "Melisa",
        created_at: "2017-04-03"
    },
    {
        name: "Amy",
        created_at: "2017-05-03"
    }];
arr.forEach(function(e){
    var month =parseInt(e.created_at.substring(5,7)) - 1;
    res[month].elems.push(e);
});
res.forEach(function (e) {
    if (e.elems.length > 0) {
        console.warn(e.month);
        e.elems.forEach(function (n) {
            console.info(n.name);
        });
        console.log('------------------------');
    }
});
console.log(res);

I hope this works :)

3 Comments

sort is es2015 method?
sort for? I just don't get it. I think you misunderstood what I want.
I changed my code,And you was write I misunderstood your purpose.
0

I think you want to group them based on the month, then use Array#reduce method with a reference object to hold the index.

const arr = [{
  name: "Alice",
  created_at: "2017-04-18"
}, {
  name: "James",
  created_at: "2017-06-30"
}, {
  name: "Melisa",
  created_at: "2017-04-03"
}, {
  name: "Amy",
  created_at: "2017-05-03"
}];

// refernce object for holding the index
var ref = {};

var res = arr.reduce(function(arr1, o) {
  //  get month value
  var m = new Date(o.created_at).getMonth();
  // check already defined in the reference array
  if (!(m in ref)) {
  // define if not defined
    ref[m] = arr1.length;
    // push an empty array
    arr1.push([]);
  }
  // push the object into the array
  arr1[ref[m]].push(o);
  // return array refernece
  return arr1;
  // set initial value as an empty array for result
}, []);


console.log(res);


// to print as in your question
var monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];

//  iterate over the generated array
res.forEach(function(v) {
  // get month name from array and generate name by concatenating the name
  console.log(monthNames[new Date(v[0].created_at).getMonth()] + ' ' + v.map(function(o) {
    return o.name;
  }).join());
})

1 Comment

fuh so complicated?
0
const roots = {}

arr.forEach((item) => {
      const formattedDate = formatDateString(item.created_at, 'MMMM yyyy')
      const currentList = roots[formattedDate] ?? []
      Object.assign(roots, { [formattedDate]: [...currentList, item] })
    })

Create a method that can format your date to a specific format, this will be your key inside the object.

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.