1

I have data that is coming from the server like this

let value = [
  {
   'commongId': 1,
   'principal': true,
   'creationDate': '2019-11-03:40:00'
   },
  {
   'commongId': 2,
   'principal': false,
   'creationDate': '2017-10-25T01:35:00'
   },
  {
   'commongId': 2,
   'principal': true,
   'creationDate': '2019-05-25T08:00:00'
   },
  {
   'commongId': 1,
   'principal': false,
   'creationDate': '2018-11-25T09:40:00'
   },
  {
   'commongId': 1,
   'principal': false,
   'creationDate': '2017-11-25T09:40:00'
   },
   {
    'commongId': 2,
    'principal': false,
    'creationDate': '2018-05-25T08:00:00'
    },
]

I want to transform it in a way that the courses are grouped by commonId, and that the principal course of each 'id' should appear first, and the rest of the courses belonging to the same commonId come after that principal course sorted by the creation date (asc).

So basically the output should be

let value = [
    {
        commongId: 1,
        principal: true,
        creationDate: '2019-11-03:40:00'
    },
    {
        commongId: 1,
        principal: false,
        creationDate: '2017-11-25T09:40:00'
    },
    {
        commongId: 1,
        principal: false,
        creationDate: '2018-11-25T09:40:00'
    },
    {
        commongId: 2,
        principal: true,
        creationDate: '2019-05-25T08:00:00'
    },
    {
        commongId: 2,
        principal: false,
        creationDate: '2017-10-25T01:35:00'
    },


    {
        commongId: 2,
        principal: false,
        creationDate: '2018-05-25T08:00:00'
    }
];

I have a working solution, which in my opinion looks horrible and too complicated.

// function to group the the courses by commonId
const groupBy = (data, keyFn) =>
    data.reduce((agg, item) => {
        const group = keyFn(item);
        agg[group] = [...(agg[group] || []), item];
        return agg;
    }, {});

let transformedValue = groupBy(courses, item => item.commonId);
//removing the keys from the array of objects
transformedValue = Object.keys(transformedValue).map(k => transformedValue[k]);
// sorting each inner array by creationDate
transformedValue = transformedValue.map(el => {
    let modified = el.sort((a, b) =>
        moment(a.creationDate).diff(moment(b.creationDate))
    );
    // pushing the principal object of each array to the top
    const foundIndex = modified.findIndex(element => element.principal);
    if (foundIndex > -1) {
        const foundElement = modified.find(element => element.principal);
        modified.splice(foundIndex, 1);
        modified.unshift(foundElement);
    }
    return modified;
});
// flattening the array to one level
transformedValue = transformedValue.flat();
// using the transformed value in the subscription
of(transformedValue).subscribe(p => {
    this.dataToShow = p;
});

2 Answers 2

2

You could use sort like this.

const value=[{commongId:1,principal:true,creationDate:"2019-11-03:40:00"},{commongId:2,principal:false,creationDate:"2017-10-25T01:35:00"},{commongId:2,principal:true,creationDate:"2019-05-25T08:00:00"},{commongId:1,principal:false,creationDate:"2018-11-25T09:40:00"},{commongId:1,principal:false,creationDate:"2017-11-25T09:40:00"},{commongId:2,principal:false,creationDate:"2018-05-25T08:00:00"},];

value.sort((a, b) => a.commongId - b.commongId 
                    || b.principal - a.principal 
                    || a.creationDate.localeCompare(b.creationDate)
           )

console.log(value)

  • The array will first be sorted based on commongId. If both have the same commongId, the subtraction will return 0. So, || will check the next expression because 0 is falsy value.

  • Then, it will be sorted based on principal. You can subtract 2 boolean values because it returns 1, -1 or 0 based on the value.

    true - false === 1
    false - true === -1
    true - true === 0
    
  • If they still have the same value for commongId and principal, the array will be sorted based on the creationDate. Since the dates are in the ISO format, you can do a string comparison using localeCompare. If the date is in some other format, you could do

    new Date(a.creationDate) - new Date(b.creationDate)
    
Sign up to request clarification or add additional context in comments.

10 Comments

hmm, when I run this in my sample, I get those with principal: true always first.. could it be because my commonId is actually not just a number like I showed in the example above, but it's actually a uuid?
@AngularDebutant that's because a.commongId - b.commongId is probably returning NaN. So, boolean comparison is proffered. a.commongId - b.commongId only works for numbers or booleans. You need to apply a similar compare logic for uuid such that it returns a postive or negative number.
@AngularDebutant What is the type of uuid? Is it a string?
yes sir! I used localCompare for the uuid and it seems to work
can you please show me how I can do so that the group of commonId with the principal having the lower creationDate goes first? So in this case those with commonId === "2" should come first. The rest of the sorting should stay the same.
|
0

Use rxjs map and lodash groupBy.

of(response).pipe(
   map(response => _groupBy(moveToFirst(response, item => item.principal === true)), 'commonId')
);

Where moveToFirst is a custom method to move the required item to Index 0.

1 Comment

Well the thing is that this moveToFirst needs to happen after grouping! because I need to move the object to the beginning of each group with the same commonId.

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.