1

I need to just get unique category from http response which Im getting as Json from API Here is my Code

export class FoodList implements OnInit{
    foodList:Food[]=[];
    p:Food[]=[];
    //name:string;

    constructor( private foodS:FoodService){}

    ngOnInit(){
    this.foodS.getAll().subscribe(allfood =>this.foodList=allfood);

    }

foodList[] giving me values but all with duplicate category I need to display only unique category .

<div class="list-group">
    <a href="#" class="list-group-item active">
        All
    </a>
    <div  *ngFor="let food of foodDetails ">
        <a href="#" class="list-group-item">{{food.category}} </a>
    </div>
</div>

service :

constructor( private http : HttpClient ){}

    getAll(): Observable<Food[]>{

    return this.http.get<Food[]>(this._url)


  }

Please suggest me any idea or how I can achieve it? Thanks

Shrys
  • 35
  • 5

2 Answers2

1

you can use map and filter but you need to change your UI

<div  *ngFor="let food of foodDetails ">
    <a href="#" class="list-group-item">{{food}} </a>
</div>

and code:

  constructor(private foodS:FoodService) { }
    ngOnInit() {
    this.foodS.getAll().subscribe(allfood=> {
      this.foodList= allfood
        .map(cat => cat.category)
        .filter((x, i, a) => x && a.indexOf(x) === i);
    });
  }
v8-E
  • 1,077
  • 2
  • 14
  • 21
  • This code is simpler than the one I wrote but... It is also not as optimized. @Shreyas said: "that is huge list" so I'm guessing that performance matters. Here you'll be mapping once onto `allfood`, then filtering onto it (which means looping through it again), and then using indexOf to know whether it's in the array or not (which is going to loop through the array as many times as there are element in the original array until it finds a match). So that's a lot. In my solution it'll only loop once over it. – maxime1992 May 23 '18 at 14:02
  • but he just need category field, and what matter is simpler solution :) ,your answer gave lots of insight thanks for that also – v8-E May 23 '18 at 14:11
  • "what matter is simpler solution" when OP said that he has a huge list, I thought perf matters actually more than simpler solution. But yeah feel free to grab one of those solutions according to your needs Shreyas :) – maxime1992 May 23 '18 at 14:14
  • valid point,but you're storing those huge values in array and again one more iteration to get just categories??,I would like to know about it just asking and not arguing with, it seems you have good understating please let me know also thank you in advance – v8-E May 23 '18 at 15:04
  • No worries I was just trying to get a productive discussion too :) I'm storing into an array the final result and the unique value within a map. The map allows me to to avoid the indexOf (which is looping over and over again on the array) while accessing through a map is just super quick. (trying generating a map of 50.000 elements and access the last one vs doing the same with an array and indexOf you'll see). It's true that it's going to loop one more time: Within the view directly. But so do you there's not much we can do about that, we need to loop over it in the view for sure. – maxime1992 May 23 '18 at 15:10
  • An from the view I do access to the category directly. – maxime1992 May 23 '18 at 15:11
  • wow so much info made me think , I can not store again same result in `one more array`, it is better to store just category in array without `JSON values` thank you guys. – Shrys May 23 '18 at 15:32
  • You wouldn't store them "again". As you have objects, they're passed by reference. If you delete them from array, they wouldn't be available from the other. It doesn't take any memory except for the array instance itself which is nothing. – maxime1992 May 23 '18 at 15:40
0

First of all, try to keep your values within a stream as often as possible.

In other words, avoid that kind of things:

.subscribe(allfood =>this.foodList=allfood);

You should rather do:

export class FoodList implements OnInit {
  foodList$: Observable<Food[]>;

  constructor(private ghumaG: GhumaGhumaluService) {}

  ngOnInit() {
    this.foodList$ = this.ghumaG
      .getAll()
      .pipe(map(foodList => Array.from(new Set(foodList))));
  }
}

2 things to notice here:
- not assigning a value at a given time, just referencing an observable
- I'm using map from rxjs to transform the value that we receive
- I'm taking advantage of Set which is pure Javascript to get an array containing only the different values

If you have objects instead of primitive types, you can just do the following instead:

Define a function to keep only unique values based on their ID:

export const keepUniqueValuesById = <T>(arr: T[]): T[] =>
  arr.reduce(
    (acc, item) => {
      if (!acc.dictionnary.has(item.id)) {
        acc.dictionnary.set(item.id, item);
        acc.finalArray.push(item);
      }

      return acc;
    },
    { finalArray: [], dictionnary: new Map<string, any>() }
  ).finalArray;

Use that function when mapping onto your data:

export class FoodList implements OnInit {
  foodList$: Observable<Food[]>;

  constructor(private ghumaG: GhumaGhumaluService) {}

  ngOnInit() {
    this.foodList$ = this.ghumaG
      .getAll()
      .pipe(map(foodList => keepUniqueValuesById(foodList)));
  }
}

In the end, in your view you can do:

<div class="list-group" *ngIf="foodList$ | async as foodList">
  <div *ngFor="let foodItem of foodList ">
    <a href="#" class="list-group-item">{{foodItem.category}} </a>
  </div>
</div>
maxime1992
  • 22,502
  • 10
  • 80
  • 121