0

I am working on a angular app. I have following array.

[
 {
        "Name": "Jack",
        "IncomingTime": "2020-06-19T11:02+00:00",
        "Outgoingtime": "2020-06-19T11:07+00:00",
  },
  {
        "Name": "Mary",
        "IncomingTime": "2020-06-19T11:05+00:00",
        "Outgoingtime": "2020-06-19T11:07+00:00",
  },
  {
        "Name": "jolly",
        "IncomingTime": "2020-06-19T11:05+00:00",
        "Outgoingtime": "2020-06-19T11:07+00:00",
  },
  {
        "Name": "Jack",
        "IncomingTime": "2020-06-19T11:05+00:00",
        "Outgoingtime": "2020-06-19T11:07+00:00",
  },
  {
        "Name": "Maria",
        "IncomingTime": "2020-06-19T11:05+00:00",
        "Outgoingtime": "2020-06-19T11:17+00:00",
  },
   {
        "Name": "Maria",
        "IncomingTime": "2020-06-19T11:05+00:00",
        "Outgoingtime": "2020-06-19T12:53+00:00",
  },
  {
        "Name": "Jack",
        "IncomingTime": "2020-06-19T11:05+00:00",
        "Outgoingtime": "2020-06-19T11:07+00:00",
  } 
]

I want to achieve a unique array. I want to sort elements in this array such that, it should first sort all elements alphabetically by Name. If names are same, then I want to sort with Incoming time. For example person with "Name': "jack' occur three times, then Jack with early incoming time should be placed in array i.e with "2020-06-19T11:02+00:00" and rest two elements of "jack" though they have same Incoming and Outgoing time are discarded(Since we have already included one with early IncomingTime in array). If with "Name" and "IncomingTime" are also same for example for "Maria", there are only two elements and "Name","IncomingTime" are same then I want to keep the element with early Outgoing time i.e the element which should be in resulting array should be of "OutgointTime": "2020-06-19T11:17+00:00" and the other one with "OutgointTime" 12:53 is discarded. How can I achieve that?

AttemptedMastery
  • 738
  • 6
  • 21
  • You can check this answer https://stackoverflow.com/questions/21857647/javascript-sort-array-twice – mcarn Jun 23 '20 at 16:41
  • For me this Arrays.sort option is not of much help as When two similar elements are found it keeps both but I want unique array as per condition mentioned above –  Jun 23 '20 at 16:45

4 Answers4

0

Not sure but something like this..

aa.sort(
    function (a, b) {

        // try names
        if (a["Name"] < b["Name"])
            return -1;
        else if (a["Name"] > b["Name"])
            return 1;

        // if names are equal compare IncomingTime
        if (a["IncomingTime"] < b["IncomingTime"])
            return -1;
        else if (a["IncomingTime"] > b["IncomingTime"])
            return 1;



        return 0;
    }
);

xMayank
  • 1,875
  • 2
  • 5
  • 19
  • I tried this, the problem I am facing with Arrays.sort if for example for Jack it will sort but first it will keep Jack with early incoming time and then other two "Jack" element with same element time as well, As I want a unique array as per description mentioned by me in question. I am running out of idea's on how to modify this to achieve array as per descriptiion –  Jun 23 '20 at 16:55
0

You could just use lodash sort by function.

import * as _ from 'lodash'

let sorted_collection = _.sortBy(collection, ['Name', 'IncomingTime', 'Outgoingtime'])

_.sortBy will let apply multiple level sorting. Do check that out.

Jay
  • 1,055
  • 2
  • 8
  • 17
  • This is an amazing thing. Could you just help me out getting a unique array as I mentioned in my description of problem. i am finding very hard to do that with this loadash –  Jun 23 '20 at 17:00
0

In TypeScript the code of Mayank Gupta is like

a.sort((a:any, b:any) => a.Name>b.Name?1:a.Name<b.Name?-1:
                       a.IncomingTime>b.IncomingTime?1: a.IncomingTime<b.IncomingTime?-1:
                       a.Outgoingtime>b.Outgoingtime?1:
                       a.Outgoingtime<b.Outgoingtime?-1:0)
Eliseo
  • 50,109
  • 4
  • 29
  • 67
  • sort change the array: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort. Well, it's true that return the array sorted too, it's the reason you usually see b=a.sort(.....), but the array itself is sorted – Eliseo Jun 23 '20 at 17:44
  • I am getting duplicates using this –  Jun 23 '20 at 17:47
  • take a llok the answer of Nicholas – Eliseo Jun 23 '20 at 18:06
0

Given your raw data,

const rawData = [
  { "Name": "Jack",  "IncomingTime": "2020-06-19T11:02+00:00", "Outgoingtime": "2020-06-19T11:07+00:00", },
  { "Name": "Mary",  "IncomingTime": "2020-06-19T11:05+00:00", "Outgoingtime": "2020-06-19T11:07+00:00", },
  { "Name": "jolly", "IncomingTime": "2020-06-19T11:05+00:00", "Outgoingtime": "2020-06-19T11:07+00:00", },
  { "Name": "Jack",  "IncomingTime": "2020-06-19T11:05+00:00", "Outgoingtime": "2020-06-19T11:07+00:00", },
  { "Name": "Maria", "IncomingTime": "2020-06-19T11:05+00:00", "Outgoingtime": "2020-06-19T11:17+00:00", },
  { "Name": "Maria", "IncomingTime": "2020-06-19T11:05+00:00", "Outgoingtime": "2020-06-19T12:53+00:00", },
  { "Name": "Jack",  "IncomingTime": "2020-06-19T11:05+00:00", "Outgoingtime": "2020-06-19T11:07+00:00", }
];

To get to what you want, we do the following:

  • First, sort the raw data by Name, then IncomingTime, then OutgoingTime.

  • Then, we iterate over the now-sorted data and keep track of the previous item we visited. Whenever we see a break in the primary sort key (a name change), we know we have the desired item (e.g., the record for the given name with the lowest incoming time) and we push that onto the set of unique items. We know we have a sequence break if

    1. There is no previous item. This is only true for the very first item in the list.
    2. If the current and previous item's Names differ.

That leads us to this solution.

const sortedData = rawData.sort(
  (x,y) => x.Name         < y.Name         ? -1 // sort by name
         : x.Name         > y.Name         ? +1 //
         : x.IncomingTime < y.IncomingTime ? -1 // then by incoming time
         : x.IncomingTime > y.IncomingTime ? +1 //
         : x.Outgoingtime < y.Outgoingtime ? -1 // then by outgoing time
         : x.Outgoingtime > y.Outgoingtime ? +1
         :                                    0 // both items compare equal
);
const uniqueData = [];
let prev;
for (const curr of sortedData) {
  if ( !prev || curr.Name !== prev.Name ) {
    uniqueData.push(curr);
  }
  prev = curr;
}
console.log(JSON.stringify(uniqueData,undefined,2));

Which produces

[
  {
    "Name": "Jack",
    "IncomingTime": "2020-06-19T11:02+00:00",
    "Outgoingtime": "2020-06-19T11:07+00:00"
  },
  {
    "Name": "Maria",
    "IncomingTime": "2020-06-19T11:05+00:00",
    "Outgoingtime": "2020-06-19T11:17+00:00"
  },
  {
    "Name": "Mary",
    "IncomingTime": "2020-06-19T11:05+00:00",
    "Outgoingtime": "2020-06-19T11:07+00:00"
  },
  {
    "Name": "jolly",
    "IncomingTime": "2020-06-19T11:05+00:00",
    "Outgoingtime": "2020-06-19T11:07+00:00"
  }
]

An arguably simpler solution might be to use Map. This accomplishes the same thing (grab the 1st entry for each name in the ordered list). The nice thing about Map is that, unlike plain objects, data is guaranteed to be returned in insertion order, so the order of the sorted list is maintained:

const sortedData = rawData.sort(
  (x,y) => x.Name         < y.Name         ? -1 // sort by name
         : x.Name         > y.Name         ? +1 //
         : x.IncomingTime < y.IncomingTime ? -1 // then by incoming time
         : x.IncomingTime > y.IncomingTime ? +1 //
         : x.Outgoingtime < y.Outgoingtime ? -1 // then by outgoing time
         : x.Outgoingtime > y.Outgoingtime ? +1
         :                                    0 // both items compare equal
);

const map = new Map();
for (const entry of sortedData) {
  const value = map.get(entry.Name);
  if (!value) {
    map.set(entry.Name, entry);
  }
}

const uniqueData = Array.from(map.values());
Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
  • hey single colon at last of sort. Did you miss something here or there is only one single colon in this line? –  Jun 23 '20 at 17:49
  • Also could you please explain your code after sort method and also I want to return result array after operations, how can I do that in this code? –  Jun 23 '20 at 17:50
  • The single colons are just nested ternary expressions: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator – Nicholas Carey Jun 23 '20 at 17:58
  • But what happens when Name is same and Incoming is different and If Incoming is also same but Outgoing is different –  Jun 23 '20 at 18:33
  • All records sharing the same name are sorted by incoming time. All records with the same name and incoming time are sorted by outgoing time. The code then walks over that ordered list and takes the first record for each name. So, for each name, you'll get that record with the lowest incoming time. If there are multiples with the same name and lowest incoming date, you'll get the record with the lowest outgoing date. – Nicholas Carey Jun 23 '20 at 22:40
  • I got the correct results with this but I don'r understand for (const curr of sortedData) { if ( !prev || curr.Name !== prev.Name ) { uniqueData.push(curr); } prev = curr; In this code I can see you are only comparing names but how it is comparing on the basis of Incoming and Outgoing time? Sorry But still I don't unnderstand it could you please explain how it is happening if you are only comparing names –  Jun 24 '20 at 01:55
  • Because the list is **ordered**, we don't need to look at the incoming/outgoing times: we did that we sorted the list. All we care about is identifying the change in names. The first record encountered for each name is the desired record. As far as the comparison goes, the test for `!prev` will succeed only for the `1st record, when `prev` is `undefined` (that being a "falsy" value. The 2nd test, `prev.Name !== curr.Name` succeeds when the name changes between records. – Nicholas Carey Jun 24 '20 at 20:19
  • hi, this code is perfectly fine. But is there is any way I can achieve same without using loop? –  Jul 03 '20 at 04:08
  • You could probably user `reduce()`... but all that accomplishes is hiding the loop. At some point you have to iterate over the collection and pick out what you need. – Nicholas Carey Jul 10 '20 at 22:07