0

This question may be similar to In Angular2 *ngFor iteration, how do I output only unique values from the array? but my question is have some more features.

I have a list of the following form

    let studentsList = [{
        name:'A',
        rollNo:1,
        mark:10
    },
    {
        name:'B',
        rollNo:2,
        mark:30
    },
    {
        name:'C',
        rollNo:3,
        mark:40
    },
    {
        name:'A',
        rollNo:1,
        mark:50
    }
  ]

The objective is to display unique name and marks from the studentList and find the unique name from it like this.

A   10
B   30
C   40

Also if the name repeats then add the marks and display.

A   60
B   30
C   40

I could filter the unique names like this

import { Pipe, PipeTransform } from '@angular/core';
import * as _ from 'lodash'; 

@Pipe({
  name: 'unique',
  pure: false
})

    export class UniquePipe implements PipeTransform {
        transform(value: any): any{
            if(value!== undefined && value!== null){
                return _.uniqBy(value, 'name');
            }
            return value;
        }
    }

then in html

<ul>
  <li *ngFor="let student of studentList| unique">
    {{student .name}}
  </li>
</ul>

Edit

The studentsList is dynamic we can add as many details needed.

Koushik Chatterjee
  • 4,106
  • 3
  • 18
  • 32
Arj 1411
  • 1,395
  • 3
  • 14
  • 36

4 Answers4

2

If you need a result array of name and marks (sum of duplicates) then you can do that with a very cleaner way in lodash (since you tagged lodash). _.groupBy will always group it by its unique parameter and group all duplivates with that parameter inside a array for each group.

  1. First, group it by name using _.groupBy
  2. Then map (.map) each group and calculate sum of marks by .sumBy

Here is an working example:

let input = [{"name":"A","rollNo":1,"mark":10},{"name":"B","rollNo":2,"mark":30},{"name":"C","rollNo":3,"mark":40},{"name":"A","rollNo":1,"mark":50}],
    res = _(input)
            .groupBy('name')
            .map((g, name) => ({name, mark: _.sumBy(g, 'mark')}))
            .value();
            
console.log(res);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Koushik Chatterjee
  • 4,106
  • 3
  • 18
  • 32
0

You could use this function into your pipe:

value.reduce((acc, ele) => {
  const existingStudent = acc.find(x => x.name === ele.name);
  if(!existingStudent) return acc.concat(ele);
  return (existingStudent.mark += ele.mark, acc);
},[])

Testing it:

var studentsList=[{name:"A",rollNo:1,mark:10},{name:"B",rollNo:2,mark:30},{name:"C",rollNo:3,mark:40},{name:"A",rollNo:1,mark:50}];

const resp = studentsList.reduce((acc, ele) => {
  const existingStudent = acc.find(x => x.name === ele.name);
  if(!existingStudent) return acc.concat(ele);
  return (existingStudent.mark += ele.mark, acc);
},[])

console.log(resp);
guijob
  • 4,413
  • 3
  • 20
  • 39
  • Thanks for the answer. If the studentList is dynamic then will this work? – Arj 1411 Mar 14 '19 at 07:05
  • 1
    This code is destructive as it mutates the items in the input list. This ain't siutable for a pipe in angular. So better use it in the place where you load and store the data. – Thomas Mar 14 '19 at 07:16
  • means no need to write pipe? – Arj 1411 Mar 14 '19 at 07:46
  • @AnandRaj *"means no need to write pipe?"* this means, when you do this right after you've loaded the data in your Controller, and store the array with the accumulated values, then you don't need a pipe that does this when rendering the data. – Thomas Mar 14 '19 at 11:19
0

An example how to accumulate your values.

let studentsList = [
  { name: 'A', rollNo: 1, mark: 10 },
  { name: 'B', rollNo: 2, mark: 30 },
  { name: 'C', rollNo: 3, mark: 40 },
  { name: 'A', rollNo: 1, mark: 50 }
];

const byName = {};
for (var student of studentsList) {
  if (student.name in byName) {
    byName[student.name].mark += student.mark;
  } else {
    byName[student.name] = { ...student };
  }
}
console.log(Object.values(byName));
Thomas
  • 11,958
  • 1
  • 14
  • 23
  • What does that 3 dots indicate? means ...? – Arj 1411 Mar 14 '19 at 09:09
  • @AnandRaj it is a spread operator. In this case, `{ ...student }` creates a shallow copy of `student` so that I don't mutate the student from the input-array when I do `byName[student.name].mark += student.mark`. – Thomas Mar 14 '19 at 11:16
0

You can group your student array on the roll number and add the marks of same roll number in an object accumulator. Using array#values() get all the values corresponding to the roll number.

let studentsList = [{ name:'A', rollNo:1, mark:10 }, { name:'B', rollNo:2, mark:30 }, { name:'C', rollNo:3, mark:40 }, { name:'A', rollNo:1, mark:50 } ],
    uniqueStudents = Object.values(studentsList.reduce((r,{name, rollNo, mark}) => {
      r[rollNo] = r[rollNo] || {name, rollNo, total : 0};
      r[rollNo].total += mark;
      return r;
    },{}));
console.log(uniqueStudents);
Hassan Imam
  • 21,956
  • 5
  • 41
  • 51