Group List by Pipe?

#1

Hi,

I’ve put the below json into an array called logs and would like to group that in a list by the created date.

{
  created : "2017-07-15 09:49:37"
  entry : "this is a log entry"
}
{
  created : "2017-05-13 11:00:21"
  entry : "this is another log entry"
}

I’m using angular2-moment, and trying to group the list by the created date using a pipe, any help appreciated.

<div *ngFor="let log of logs">
	  <ion-list-header>
	  	  {{log.created | amTimeAgo}}
	  </ion-list-header>
	 <ion-item>
	 	 {{log.entry}}
	 </ion-item>
</div>

Thanks

#2

In my opinion this is too much to do via a pipe, as it’s not terribly performant. I would instead group in your controller and then display that in your template.

Another note is that I’d recommend using date-fns instead of moment.js, as date-fns works with tree shaking resulting in a smaller bundle.

1 Like
#3

Thanks, that doesn’t sound that easy, if I wanted to order weekly for example…

#4

Honestly, Ionic is hard. A lot of people start here without much background, thinking it’s like Java. But you really ought to know the basics of Angular and Typescript before progamming in Ionic. So yeah, it will take some work before you can write performant code.

#5

Ok I’ve now done the weekly grouping in PHP which returns the below Json,

"payload": {
"logs": {
  "19": [
    "[{\"entry\":"this is an entry",\"created\":\"2017-05-12 09:51:18\"}]"
  ],
  "28": [
    "[{\"entry\":"this is an entry",\"created\":\"2017-07-15 09:50:50\"}]",
    "[{\"entry\":"this is an entry",\"created\":\"2017-07-15 09:51:18\"}]"
  ]
}

I receive it like so,

private weeks: any[];

this.api.getLogs().subscribe(
      data => {
        // Success
        this.weeks = data.payload.logs;
      },
      err => {
        //Err
      }
    );

and I attempt to use it in Ionic 2,

<div *ngFor="let week of weeks">
    <div *ngFor="let log of week">
        {{log.created}}
    </div>
</div>

but that gives me the below error,

Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays.

Any help appreciated.

Thanks

#6

First step is to use types. Instead of any[], type weeks with the precise type of object you receive from the api call.

1 Like
#7

Indeed, it’ll hurt in the long term to use any. Second, logs is an object and not an array, and you can’t loop over an object with ngFor.

1 Like
#8

Shared this a while back - sortByDatetime

Will return an array for you to loop through, you can simply take it out of the pipe and use it inside your component.

#9

Not particularly hard. There are a bunch of ways to go about this, but my first instinct would be startOfWeek(). Something like this:

export interface LogEntry {
  created: string;
  entry: string;
}

export interface LogWeeks {
  [week: string]: LogEntry[];
}

groupLogsByWeek(logs: LogEntry[]): LogWeeks {
  let rv = {} as LogWeeks;
  logs.forEach((le) => {
    let week = startOfWeek(le.created);
    if (!rv[week]) {
      rv[week] = [];
    }
    rv[week].push(le);
  }
  return rv;
}
1 Like
#10

@rapropos that’s superb mate, I can’t thank you enough. I’ve now got them grouped weekly.

How would I get around the issue of binding in the html template now? I’m still hitting the NgFor only bindable to arrays?

Many Thanks.

#11

Can you change the PHP to make logs an array rather than an object?

#12

Depends on what you want the array to look like. Something like this might be a starting point:

let weeks = groupLogsByWeek(logs);
let weekar = weeks.keys().sort().map((week) => {
  return {week: week, entries: weeks[week]};
});
1 Like
#13

Thanks, that returns,

Cannot invoke an expression whose type lacks a call signature. Type LogEntry[] has no compatible call signatures.

I tried googling to understand the issue but I’m at a loss here.

#14

That error message seems strange, but fooling around with node, perhaps one may need to substitute Object.keys(weeks) where I initially had weeks.keys(). Oh JavaScript, how I despise thee.

1 Like
#15

@rapropos thanks for the help, added a pipe to get the values from your solution and it’s working a treat now