From 260bae963f04766a19fcaa61085e9699dd194910 Mon Sep 17 00:00:00 2001 From: "HyunWoo Lee (Nunu Lee)" <54518925+l2hyunwoo@users.noreply.github.com> Date: Mon, 17 Jun 2024 18:39:42 +0900 Subject: [PATCH] feat(maxBy): Add maxBy function that select element that have max value by given condition in array (#64) * feat(maxBy): Add maxBy function * feat(maxBy): Add benchamark test * feat(maxBy): Add unit test of maxBy * docs(maxBy): Add reference of maxBy function * feat(maxBy): use for-each loop instead of index-increasing fashion * fix(maxBy): Change code by code review * fix(maxBy): Changed by code review, all tests are fine * fix(maxBy): remove explicit undefined return * fix(maxBy): Fix reference due to signature modification * Update src/math/maxBy.ts --------- Co-authored-by: Sojin Park --- benchmarks/maxBy.bench.ts | 23 +++++++++++++++++++ docs/ko/reference/math/maxBy.md | 27 +++++++++++++++++++++++ docs/reference/math/maxBy.md | 27 +++++++++++++++++++++++ src/math/index.ts | 1 + src/math/maxBy.spec.ts | 39 +++++++++++++++++++++++++++++++++ src/math/maxBy.ts | 32 +++++++++++++++++++++++++++ 6 files changed, 149 insertions(+) create mode 100755 benchmarks/maxBy.bench.ts create mode 100644 docs/ko/reference/math/maxBy.md create mode 100644 docs/reference/math/maxBy.md create mode 100644 src/math/maxBy.spec.ts create mode 100644 src/math/maxBy.ts diff --git a/benchmarks/maxBy.bench.ts b/benchmarks/maxBy.bench.ts new file mode 100755 index 00000000..c4621ba7 --- /dev/null +++ b/benchmarks/maxBy.bench.ts @@ -0,0 +1,23 @@ +import { bench, describe } from 'vitest'; +import { maxBy as maxByToolkit } from 'es-toolkit'; +import { maxBy as maxByLodash } from 'lodash'; + +describe('maxBy', () => { + bench('es-toolkit', () => { + const people = [ + { name: 'Mark', age: 25 }, + { name: 'Nunu', age: 30 }, + { name: 'Overmars', age: 20 }, + ]; + maxByToolkit(people, person => person.age); + }); + + bench('lodash', () => { + const people = [ + { name: 'Mark', age: 25 }, + { name: 'Nunu', age: 30 }, + { name: 'Overmars', age: 20 }, + ]; + maxByLodash(people, person => person.age); + }); +}); diff --git a/docs/ko/reference/math/maxBy.md b/docs/ko/reference/math/maxBy.md new file mode 100644 index 00000000..862f70cc --- /dev/null +++ b/docs/ko/reference/math/maxBy.md @@ -0,0 +1,27 @@ +# maxBy + +주어진 배열 내의 요소들 중에서 조건에 따라 최대값을 가지는 첫 번째 요소를 선택하는 함수에요. + +배열이 비어있지 않다면 조건에 따라 최대값을 가지는 첫 번째 요소를 반환하고, 비어있다면 `undefined`를 반환해요. + +## 인터페이스 + +```typescript +function maxBy(elements: T[], selector: (element: T) => number): T +``` + +### 파라미터 + +- `elements`: 검색할 요소들의 배열 +- `selector`: 요소를 받아서 객체의 속성을 반환하는 함수 + +### 반환값 + +함수의 최대값을 가지는 배열의 첫 번째 요소. 만약 배열이 비어있다면 `undefined`를 반환해요. + +### 예시 + +```typescript +maxBy([{ a: 1 }, { a: 2 }, { a: 3 }], x => x.a) // 결과: { a: 3 } +maxBy([], x => x.a) // 결과: undefined +``` diff --git a/docs/reference/math/maxBy.md b/docs/reference/math/maxBy.md new file mode 100644 index 00000000..18e64114 --- /dev/null +++ b/docs/reference/math/maxBy.md @@ -0,0 +1,27 @@ +# maxBy + +Selects the first element of a list that has the maximum value of a function. + +If the list is empty, returns `undefined`. + +## Signature + +```typescript +function maxBy(elements: T[], selector: (element: T) => number): T +``` + +### Parameters + +- `elements`: an array of elements to search through. +- `selector`: a function that takes an element and returns a number that the property of the object. + +### Returns + +The first element of the list that has the maximum value of the function. If the list is empty, returns `undefined`. + +### Example + +```typescript +maxBy([{ a: 1 }, { a: 2 }, { a: 3 }], x => x.a) // Returns: { a: 3 } +maxBy([], x => x.a) // Returns: undefined +``` diff --git a/src/math/index.ts b/src/math/index.ts index c8d91443..c8081c48 100644 --- a/src/math/index.ts +++ b/src/math/index.ts @@ -3,3 +3,4 @@ export { random } from './random'; export { randomInt } from './randomInt'; export { round } from './round'; export { sum } from './sum'; +export { maxBy } from './maxBy'; diff --git a/src/math/maxBy.spec.ts b/src/math/maxBy.spec.ts new file mode 100644 index 00000000..514dc142 --- /dev/null +++ b/src/math/maxBy.spec.ts @@ -0,0 +1,39 @@ +import { describe, expect, it } from "vitest"; +import { maxBy } from './maxBy'; + +describe('maxBy', () => { + it('maxBy selects one max value in array', () => { + const people = [ + { name: 'Mark', age: 25 }, + { name: 'Nunu', age: 30 }, + { name: 'Overmars', age: 20 }, + ]; + const result = maxBy(people, person => person.age); + expect(result).toEqual({ name: 'Nunu', age: 30 }); + }); + + it('if there are two max values, first one is selected', () => { + const people = [ + { name: 'Mark', age: 25 }, + { name: 'Nunu', age: 30 }, + { name: 'Overmars', age: 30 }, + ]; + const result = maxBy(people, person => person.age); + expect(result).toEqual({ name: 'Nunu', age: 30 }); + }); + + it('if array is single-element, return unique element of array', () => { + const people = [ + { name: 'Mark', age: 25 }, + ]; + const result = maxBy(people, person => person.age); + expect(result).toEqual({ name: 'Mark', age: 25 }); + }); + + it('if array is empty, return undefined', () => { + type Person = { name: string, age: number }; + const people: Person[] = []; + const result = maxBy(people, person => person.age); + expect(result).toBeUndefined(); + }); +}); diff --git a/src/math/maxBy.ts b/src/math/maxBy.ts new file mode 100644 index 00000000..2618c9a1 --- /dev/null +++ b/src/math/maxBy.ts @@ -0,0 +1,32 @@ +/** + * Returns the element of the specified array that has the maximum value, according to the specified selector. + * + * This function takes an array of elements and a selector function, and returns the element with the maximum value according to the selector. + * + * @param {T[]} elements - An array of elements to be compared. + * @param {(element: T) => number} selector - A function that takes an element and returns a number. + * @returns {T} The element with the maximum value according to the selector. + * + * @example + * const people = [ + * { name: 'Mark', age: 25 }, + * { name: 'Nunu', age: 30 }, + * { name: 'Overmars', age: 20 } + * ]; + * const result = maxBy(people, person => person.age); + * // result will be { name: 'Nunu', age: 30 } + */ +export function maxBy(elements: T[], selector: (element: T) => number): T { + let maxElement = elements[0]; + let max = -Infinity; + + for (const element of elements) { + const value = selector(element); + if (value > max) { + max = value; + maxElement = element; + } + } + + return maxElement; +}