Skip to content

Commit

Permalink
feat(utils): 新增 orderByRules
Browse files Browse the repository at this point in the history
  • Loading branch information
fjc0k committed Oct 6, 2024
1 parent e6f5c7c commit faacc14
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export * from './move'
export * from './ms'
export * from './omitStrict'
export * from './onceMeanwhile'
export * from './orderByRules'
export * from './parseDataUrl'
export * from './parseUrlQueryString'
export * from './pascalCase'
Expand Down
113 changes: 113 additions & 0 deletions src/utils/orderByRules.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { orderByRules } from './orderByRules'

const strData = ['hello', 'fjc', '20', '花间一壶酒', '=']

const numData = [-5, 4, 0, 1.2, 20]

const arrData = [
{ a: 'x', b: 3 },
{ a: 'y', b: 4 },
{ a: 'x', b: 1 },
{ a: 'z', b: 1 },
{ a: 'y', b: 2 },
]

describe('orderByRules', () => {
test('desc 正常', () => {
expect(
orderByRules(strData, {
iteratee: item => item.length,
type: 'desc',
}),
).toEqual(['hello', '花间一壶酒', 'fjc', '20', '='])

expect(
orderByRules(numData, {
iteratee: item => item,
type: 'desc',
}),
).toEqual([20, 4, 1.2, 0, -5])

expect(
orderByRules(arrData, {
iteratee: item => item.b,
type: 'desc',
}),
).toEqual([
{ a: 'y', b: 4 },
{ a: 'x', b: 3 },
{ a: 'y', b: 2 },
{ a: 'x', b: 1 },
{ a: 'z', b: 1 },
])
})

test('asc 正常', () => {
expect(
orderByRules(strData, {
iteratee: item => item.length,
type: 'asc',
}),
).toEqual(['=', '20', 'fjc', 'hello', '花间一壶酒'])

expect(
orderByRules(numData, {
iteratee: item => item,
type: 'asc',
}),
).toEqual([-5, 0, 1.2, 4, 20])

expect(
orderByRules(arrData, {
iteratee: item => item.b,
type: 'asc',
}),
).toEqual([
{ a: 'x', b: 1 },
{ a: 'z', b: 1 },
{ a: 'y', b: 2 },
{ a: 'x', b: 3 },
{ a: 'y', b: 4 },
])
})

test('多个规则正常', () => {
expect(
orderByRules(arrData, [
{
iteratee: item => item.b,
type: 'desc',
},
{
iteratee: item => item.a.charCodeAt(0),
type: 'asc',
},
]),
).toEqual([
{ a: 'x', b: 3 },
{ a: 'x', b: 1 },
{ a: 'y', b: 4 },
{ a: 'y', b: 2 },
{ a: 'z', b: 1 },
])

expect(
orderByRules(arrData, [
{
iteratee: item => item.b,
type: 'desc',
},
{
iteratee: item => item.a.charCodeAt(0),
type: 'desc',
},
]),
).toEqual([
{ a: 'z', b: 1 },
{ a: 'y', b: 4 },
{ a: 'y', b: 2 },
{ a: 'x', b: 3 },
{ a: 'x', b: 1 },
])
})
})
64 changes: 64 additions & 0 deletions src/utils/orderByRules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { castArray } from 'lodash-uni'
import { OneOrMore } from '../types'

export type OrderByRulesRule<T> = {
/**
* 迭代函数。
*
* @param item 项目
* @returns 返回参与排序计算的值
*/
iteratee: (item: T) => any
/**
* 类型。
*/
type: 'asc' | 'desc'
}

/**
* 允许指定一个或多个规则对数据进行排序。
*
* @param data 要排序的数据
* @param rules 一个或多个规则
* @returns 返回排序后的数据
* @example
* ```ts
* orderByRules(
* ['x', 'xyz', 'xy'],
* {
* iteratee: item => item.length,
* type: 'desc',
* },
* )
* // => ['xyz', 'xy', 'x']
* ```
*/
export function orderByRules<T>(
data: T[],
rules: OneOrMore<OrderByRulesRule<T>>,
): T[] {
return castArray(rules).reduce<T[]>((orderedData, rule) => {
const cachedKeys: T[] = []
const cachedValues: any[] = []
const cachedIteratee: OrderByRulesRule<T>['iteratee'] = item => {
const index = cachedKeys.indexOf(item)
if (index === -1) {
const value = rule.iteratee(item)
cachedKeys.push(item)
cachedValues.push(value)
return value
}
return cachedValues[index]
}

const isAsc = rule.type === 'asc'

orderedData.sort((a, b) => {
a = cachedIteratee(a)
b = cachedIteratee(b)
return a === b ? 0 : a > b ? (isAsc ? 1 : -1) : isAsc ? -1 : 1
})

return orderedData
}, data.slice())
}

0 comments on commit faacc14

Please sign in to comment.