1

So I have this array of pilots :

var pilots = [
  {
    id: 10,
    name: "Poe Dameron",
    years: 14,
  },
  {
    id: 2,
    name: "Temmin 'Snap' Wexley",
    years: 30,
  },
  {
    id: 41,
    name: "Tallissan Lintra",
    years: 16,
  },
  {
    id: 99,
    name: "Ello Asty",
    years: 22,
  }
];

And I want to print the most experienced pilot. By using reduce() I can print the most experienced one. But this reduce function will print the last pilot with most experience. In the above array the reduce works perfectly:

var mostExpPilot = pilots.reduce(function (oldest, pilot) {
  return (oldest.years || 0) > pilot.years ? oldest : pilot;
}, {});

console.log(mostExpPilot); // Prints { id: 2, name: 'Temmin \'Snap\' Wexley', years: 30 }

If we have 2 or more pilots with the same experience and I want to print both occurrences. For example we have the below array where there are two pilots with the same years of experience. The reduce function that is implemented it prints only the last pilot.

var pilots = [
    {
      id: 10,
      name: "Poe Dameron",
      years: 14,
    },
    {
      id: 2,
      name: "Temmin 'Snap' Wexley",
      years: 30,
    },
    {
      id: 41,
      name: "Tallissan Lintra",
      years: 30,
    },
    {
      id: 99,
      name: "Ello Asty",
      years: 22,
    }
  ];



//find the most experienced pilot 
var mostExpPilot = pilots.reduce(function (oldest, pilot) {
    return (oldest.years || 0) > pilot.years ? oldest : pilot;
  }, {});

console.log(mostExpPilot); // prints { id: 41, name: 'Tallissan Lintra', years: 30 }

Can I do it with reduce() ? If yes, could you please let me know? If no, how can I do that? Thanks.**

FZs
  • 16,581
  • 13
  • 41
  • 50
klevisx
  • 175
  • 1
  • 2
  • 12
  • use "filterAll" function instead of "reduce" function. – RK_15 Feb 21 '19 at 11:32
  • You can do that with reduce as long as you change the return type to an array instead of an object. Besides, it would make far more sense to first **group** the results by age, then **sort** them by year and return the desired instance. You are actually using reduce to **sort** the items. – briosheje Feb 21 '19 at 11:33
  • Why does it have to be done with `reduce`? – Dexygen Feb 21 '19 at 11:38
  • @GeorgeJempty I am just asking. I saw the example and thought about this problem. – klevisx Feb 21 '19 at 11:39
  • @GeorgeJempty: It's probably faster doing it with `reduce` than in another way, since `reduce` does it in a single pass; the downside is that it is somewhat less readable than finding maximum and then using filter, or grouping then finding maximum, or sorting then taking the top equal items. – Amadan Feb 21 '19 at 11:41
  • @Amadan ohh I see. this looks fancy. I was just thinking at the first moment but know I see that it may have some advantages. :O – klevisx Feb 21 '19 at 11:43
  • @Amadan I agree reduce does it in one pass – Dexygen Feb 21 '19 at 11:52

4 Answers4

3

You can, though it's not very common idiom:

const oldestPilots = pilots => pilots.reduce((oldest, pilot) => {
  if (!oldest.length || oldest[0].years < pilot.years) return [pilot];
  if (oldest[0].years == pilot.years) return [...oldest, pilot];
  return oldest;
}, [])
Amadan
  • 191,408
  • 23
  • 240
  • 301
3

If we have 2 or more pilots with the same experience and I want to print both occurrences.

To do that, you'll need to return an array. If you're going to return an array sometimes, it's best to return an array always (even if it will have only one entry in it).

You'd do it by returning an array with just the current pilot if that pilot has more experience than the previously-known pilot, adding the current pilot to the array if they have the same experience, or keeping the previously-known pilot otherwise:

var pilots = [
    {
      id: 10,
      name: "Poe Dameron",
      years: 14,
    },
    {
      id: 2,
      name: "Temmin 'Snap' Wexley",
      years: 30,
    },
    {
      id: 41,
      name: "Tallissan Lintra",
      years: 30,
    },
    {
      id: 99,
      name: "Ello Asty",
      years: 22,
    }
];

// Find the most experienced pilots
var mostExpPilot = pilots.reduce(function(most, pilot) {
    var mostYears = most[0] ? most[0].years : -1;
    if (mostYears < pilot.years) {
        return [pilot];
    }
    if (mostYears === pilot.years) {
        most.push(pilot);
    }
    return most;
}, []);

console.log(mostExpPilot);
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • If you see `null` at the end of `reduce`, hit refresh. `null` would be a bug if the pilots array were empty. (A bug in that the result wouldn't always be an array.) – T.J. Crowder Feb 21 '19 at 11:43
  • 1
    Crowder Thanks. Got it! – klevisx Feb 21 '19 at 11:46
  • 1
    That's a bit faster than mine, due to nice `push`. Watch out for people with negative ages. :P – Amadan Feb 21 '19 at 11:50
  • @Amadan - Is it? I guess because of the spread, which uses an iterator. Yours is more FP-like, though, which fits nicely with `reduce`. :-) – T.J. Crowder Feb 21 '19 at 11:53
  • @Amadan - Yeah, I kinda meant both. (Pity the OP decided to unaccept yours and accept one that's nearly identical, just harder to read...) – T.J. Crowder Feb 21 '19 at 11:57
1
var mostExpPilot = pilots.reduce(function (oldest, pilot) {
    return oldest[0].years < pilot.years ? [pilot] : (oldest[0].years == pilot.years ? [...oldest, pilot] : oldest);
  }, [pilots[0]]);

console.log(mostExpPilot);
RK_15
  • 929
  • 5
  • 11
1

The following gets it all down to two lines but takes more passes than reduce (which takes just one). Nevertheless if the sample size will always be small the readability may be worth it:

const mostYearsExperience = Math.max.apply({}, pilots.map(p => p.years));
const mostExperiencedPilots = pilots.filter(p => p.years === mostYearsExperience);
Dexygen
  • 12,287
  • 13
  • 80
  • 147