Skip to content

Instantly share code, notes, and snippets.

@karol-majewski
Last active November 21, 2020 19:59
Show Gist options
  • Save karol-majewski/d7b429f1349c614626f79e2d4c66f636 to your computer and use it in GitHub Desktop.
Save karol-majewski/d7b429f1349c614626f79e2d4c66f636 to your computer and use it in GitHub Desktop.
interface Milestone {
label: string;
count: number;
}
interface Options {
separator?: string;
}
const format = (milestones: Milestone[], options: Options = { separator: ' ' }): string =>
milestones
.filter(({ count }) => count > 0)
.map(({ count, label }) => `${count} ${label}`)
.join(options.separator)
console.log(
format([
{ label: 'years', count: 1 },
{ label: 'months', count: 2 },
{ label: 'days', count: 32 },
{ label: 'seconds', count: 2 },
])
)
@karol-majewski
Copy link
Author

karol-majewski commented Aug 6, 2020

Improved design:

interface Milestrone {
  getLabel: (count: number) => string;
  count: number;
}

const format = (milestones: Milestrone[], separator: string = ' '): string =>
  milestones
    .filter(({ count }) => count > 0)
    .map(({ count, getLabel }) => `${count} ${getLabel(count)}`)
    .join(separator)

console.log(
  format([
      { getLabel: count => count === 1 ? 'year' : 'years', count: 20 },
      { getLabel: count => count === 1 ? 'month' : 'months', count: 10 },
      { getLabel: count => count === 1 ? 'day' : 'days', count: 12 },
      { getLabel: count => count === 1 ? 'second' : 'seconds', count: 42 },
  ])
)
  • Can add different milestones (seconds, milliseconds)
  • Presentation is separate from data
  • Pluralization ready
  • Provide a custom separator

@karol-majewski
Copy link
Author

karol-majewski commented Aug 6, 2020

Original design:

function parseDate(input: number[]) {
  const [years, months, days] = input;

  return ([
      ...years ? [`${years} years`]: [],
      ...months ? [`${months} months`]: [],
      ...days ? [`${days} days`]: [],
  ]).join(' ')
};
  • Unsafe array destructuring (the tuple must have 3 elements, it's not any array)
  • Ambiguity: should 0 be preserved? Implicit coercion eats zeros (they're falsy)
  • Relies on a... clever solution (desctructuring [] returns nothing)
  • The default separator is baked in
  • Allows invalid values (80 seconds, 14 months)

@karol-majewski
Copy link
Author

interface Milestone {
  label: React.ReactElement | ((count: number) => React.ReactElement);
  count: number;
}

const format = (milestones: Milestone[], separator: string = ' '): string =>
  milestones
    .filter(({ count }) => count > 0)
    .map(({ count, label }) =>
      React.isValidElement(label)
        ? label
        : label(count)
    )
    .join(separator);

const milestones: Milestone[] = [
  { count: 20, label: count => t('app:milestone.year', { count })},
  { count: 10, label: count => t('app:milestone.month', { count })},
  { count: 12, label: count => t('app:milestone.day', { count })},
  { count: 42, label: count => t('app:milestone.second', { count })},
]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment