Home > Back-end >  How to reduce to find lowest date for each year from section of dates
How to reduce to find lowest date for each year from section of dates

Time:02-18

Right now I have a for-loop going over the dates. I first sort the order and then pluck the first date of each year, but I feel like this could be used with .reduce() instead and converting the results with Object.values(result) would return me the array or 3 dates.

Reducing with one tenerary would get me the lowest overall date. But I am stuck where I need to return the lowest date from each year within the section.

.reduce((a,b) =>
a = moment(a) < moment(a) ? a : b
return a
)

If there are better ways to achive the end result I am all ears.

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  firstDatesOfYear = [];

  data = [
    '2022-02-14T00:00:00',
    '2021-06-14T00:00:00',
    '2021-06-21T00:00:00',
    '2021-11-01T00:00:00',
    '2022-01-10T00:00:00',
    '2022-01-03T00:00:00',
    '2020-07-20T00:00:00',
    '2020-08-31T00:00:00',
    '2020-09-07T00:00:00',
    '2021-02-22T00:00:00',
    '2021-06-07T00:00:00',
    '2022-01-17T00:00:00',
    '2022-01-24T00:00:00',
    '2022-01-31T00:00:00',
  ];

  // find first date of each year from non sorted list of ISO dates
  // result should be ["2020-07-20T00:00:00", "2021-02-22T00:00:00", "2022-01-03T00:00:00"]

  constructor() {
    let temp = this.data
      .map((day) => moment(day))
      .sort((a, b) => a.unix() - b.unix());
    // stuck here, instead of doing the whole following below - should use .reduce()?

    console.log(temp);

    for (let i = 0; i < temp.length; i  ) {
      if (i == 0) {
        this.firstDatesOfYear.push(temp[i]);
      } else if (moment(temp[i]).year() != moment(temp[i   1]).year()) {
        this.firstDatesOfYear.push(temp[i   1]);
      }
    }
  }
}

Problem: https://stackblitz.com/edit/angular-moment-example-z7m92c?file=app/app.component.ts

CodePudding user response:

I don't pretend to know everything that's going on, but an easy way to find the first date is with .sort().

const data = [
  "2022-02-14T00:00:00",
  "2021-06-14T00:00:00",
  "2021-06-21T00:00:00",
  "2021-11-01T00:00:00",
  "2022-01-10T00:00:00",
  "2022-01-03T00:00:00",
  "2020-07-20T00:00:00",
  "2020-08-31T00:00:00",
  "2020-09-07T00:00:00",
  "2021-02-22T00:00:00",
  "2021-06-07T00:00:00",
  "2022-01-17T00:00:00",
  "2022-01-24T00:00:00",
  "2022-01-31T00:00:00",
];

const firstDate = data.sort((a, b) => new Date(a) - new Date(b))[0];

console.log(firstDate);
console.log(String(firstDate)); // if you want a string

CodePudding user response:

The idea to use .reduce() is one possible way to achieve the objective. Combine it with Object.values() and below is one possible implementation:

const origData = [
    '2022-02-14T00:00:00',
    '2021-06-14T00:00:00',
    '2021-06-21T00:00:00',
    '2021-11-01T00:00:00',
    '2022-01-10T00:00:00',
    '2022-01-03T00:00:00',
    '2020-07-20T00:00:00',
    '2020-08-31T00:00:00',
    '2020-09-07T00:00:00',
    '2021-02-22T00:00:00',
    '2021-06-07T00:00:00',
    '2022-01-17T00:00:00',
    '2022-01-24T00:00:00',
    '2022-01-31T00:00:00',
  ];
  
  const getFirstDates = (arr = origData) => (
    Object.values(
      arr.reduce(
        (fin, itm) => ({
          ...fin,
          [moment(itm).format('YYYY')]: (
            fin[moment(itm).format('YYYY')] &&
            fin[moment(itm).format('YYYY')] < itm
              ? fin[moment(itm).format('YYYY')]
              : itm
          )
        }),
        {}
      )
    )
  );
  
  console.log(getFirstDates());
<script src="https://unpkg.com/[email protected]/moment.js"></script>

Explanation

  • Use .reduce() to obtain an object with props as the year and values as the earliest date of that year
  • Use Object.values() to extract just the values (which will be earliest date on each given year)

How reduce works

  • fin is the aggregator & is initially set to {} (empty object)
  • itm is the iterator (ie, each date in the origData array)
  • If the year of the current itm already exists, then check if the existing value in the aggregator fin[moment(itm).format('YYYY')] is lesser than itm (ie, whether the date is earlier than the one in itm)
  • If the existing date is earlier, do nothing (ie, return it as-is)
  • Otherwise, repopulate the value with itm
  • Related