skip to Main Content

I want to generate a list of years that should be dynamic. I want to show the current year, previous 3 years and next 4 years along with an empty object at the start. The output should be [2020, 2021, 2022, 2023, 2023, 2024, 2025, 2026, 2027]

I have a working function, but I’m wondering if there is a more efficient / preferment approach. It feels like too much code for a simple function.

const generateYears = () => {
  const previousYears = [...Array(4)].map(
    (a, b) => new Date().getFullYear() - b,
  );
  const futureYears = [...Array(5)].map((a, b) => new Date().getFullYear() + b);

  const years = new Set([...previousYears, ...futureYears].sort());

  const yearsObj = [...years].map((year: number) => {
    return {
      value: year.toString(),
      name: year.toString(),
    };
  });

  yearsObj.unshift({
    value: '',
    name: '',
  });

  return yearsObj;
};

3

Answers


  1. I would use a range helper function for that. Something like:

    const range = (len, startWith = 0) => 
      [...Array(len + startWith).keys()].slice(startWith);
    const getYears = (fromYear, nPrev, nNext) => 
      range( (nPrev + nNext + 1), fromYear - nPrev );
    
    console.log(getYears(new Date().getFullYear(), 3, 4));

    See also

    Login or Signup to reply.
  2. Just made this is different way using ES6,

    able to create the array as required in single line but adding the same was made manually,

    let data=[{value: '',name: ''},...[...Array(7)].map((a, b) => new Date().getFullYear() + b-3).map((e)=>{ return {value: e, name: e }})];
    console.log(data);
    

    this is the exact format as shared in the example, modified as per the output of the question.

    if it is all about array,

    let data=[...Array(7)].map((a, b) => new Date().getFullYear() + b-3)
    console.log(data);
    
    Login or Signup to reply.
  3. I will present a series of progressive refactorings, but here is the final code:

    const range = (length: number, start = 0) =>
      Array.from({ length }, (_, index) => start + index);
    
    const generateYears = (
      previousYearsCount: number,
      futureYearsCount: number,
      year = new Date().getFullYear()
    ) => {
      const years = range(
        previousYearsCount + futureYearsCount + 1,
        year - previousYearsCount
      );
    
      const yearObjects = [
        {
          value: "",
          name: "",
        },
        ...years.map((year) => ({
          value: year.toString(),
          name: year.toString(),
        })),
      ];
    
      return yearObjects;
    };
    
    generateYears(3, 4);
    

    First, instead of creating multiple ranges, merging them, and deduplicating values, you can create a single range.
    I would do this with a helper function, for clarity:

    const range = (length: number, start = 0) =>
      Array.from({ length }, (_, index) => start + index)
    

    This creates an array of length length containing a sequence of numbers starting at start.

    With some other slight refactors (how the final array is constructed, etc.), that makes it something like this:

    const generateYears = () => {
      const previousYearsCount = 3;
      const futureYearsCount = 4;
    
      const years = range(
        previousYearsCount + futureYearsCount + 1,
        new Date().getFullYear() - previousYearsCount
      )
    
      const yearObjects = [
        {
          value: '',
          name: '',
        },
        ...years.map((year) => ({
          value: year.toString(),
          name: year.toString(),
        }))
      ];
    
      return yearObjects;
    };
    

    To make this more reusable, you could allow the "center" year and number of previous and future years to be passed as parameters:

    const generateYears = (
      year: number,
      previousYearsCount: number,
      futureYearsCount: number
    ) => {
      const years = range(
        previousYearsCount + futureYearsCount + 1,
        year - previousYearsCount
      );
    
      // ...
    };
    
    generateYears(new Date().getFullYear(), 3, 4);
    

    If you mostly will use the current year as the "center", you could move that parameter to the end and make it optional:

    const generateYears = (
      previousYearsCount: number,
      futureYearsCount: number,
      year = new Date().getFullYear()
    ) => {
      // ...
    }
    
    generateYears(3, 4); // Use the current year as the "center".
    generateYears(3, 4, 2017);
    

    Aside: If you want a more functional programming "feel", you could do it something like this:

    const generateYears = (
      year: number,
      previousYearsCount: number,
      futureYearsCount: number
    ) => [
      {
        value: "",
        name: "",
      },
    
      ...range(
        previousYearsCount + futureYearsCount + 1,
        year - previousYearsCount
      ).map((year) => ({
        value: year.toString(),
        name: year.toString(),
      })),
    ];
    

    (I arrived at this independently, but credit to @Kooilnc for getting there first.)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search