From 227af3723688abcd664e0b0f12e919d1b4252b6c Mon Sep 17 00:00:00 2001 From: okxiaoliang4 <353742991@qq.com> Date: Sat, 11 Jun 2022 02:24:39 +0800 Subject: [PATCH 1/6] feat(useSorted): new function --- packages/core/index.ts | 1 + packages/core/useSorted/demo.vue | 60 ++++++++++++++++++++ packages/core/useSorted/index.md | 49 +++++++++++++++++ packages/core/useSorted/index.test.ts | 79 +++++++++++++++++++++++++++ packages/core/useSorted/index.ts | 51 +++++++++++++++++ 5 files changed, 240 insertions(+) create mode 100644 packages/core/useSorted/demo.vue create mode 100644 packages/core/useSorted/index.md create mode 100644 packages/core/useSorted/index.test.ts create mode 100644 packages/core/useSorted/index.ts diff --git a/packages/core/index.ts b/packages/core/index.ts index 8d8cc39019d..22df23e6c94 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -94,6 +94,7 @@ export * from './useScroll' export * from './useScrollLock' export * from './useSessionStorage' export * from './useShare' +export * from './useSorted' export * from './useSpeechRecognition' export * from './useSpeechSynthesis' export * from './useStepper' diff --git a/packages/core/useSorted/demo.vue b/packages/core/useSorted/demo.vue new file mode 100644 index 00000000000..346b13f53c1 --- /dev/null +++ b/packages/core/useSorted/demo.vue @@ -0,0 +1,60 @@ + + + diff --git a/packages/core/useSorted/index.md b/packages/core/useSorted/index.md new file mode 100644 index 00000000000..119eb546f82 --- /dev/null +++ b/packages/core/useSorted/index.md @@ -0,0 +1,49 @@ +--- +category: Sort +--- + +# useSorted + +reactive sort array + +## Usage + +```ts +import { quickSort, useSorted } from '@vueuse/core' + +// general sort +const sorted = useSorted([10, 3, 5, 7, 2, 1, 8, 6, 9, 4]) + +// object sort +const objArr = [{ + name: 'John', + age: 40, +}, { + name: 'Jane', + age: 20, +}, { + name: 'Joe', + age: 30, +}, { + name: 'Jenny', + age: 22, +}] +const objSorted = useSorted(objArr, quickSort, { + compareFn: (a, b) => a.age - b.age, +}) +``` + +### useSortedWrapFn + +```ts +const wrapFn = useSortedWrapFn(quickSort, { + compareFn: (a, b) => a.age - b.age, +}) +const sorted = wrapFn(objArr) + +// or +const wrapFn = useSortedWrapFn(quickSort) +const sorted = wrapFn(objArr, { + compareFn: (a, b) => a.age - b.age, +}) +``` diff --git a/packages/core/useSorted/index.test.ts b/packages/core/useSorted/index.test.ts new file mode 100644 index 00000000000..7ec59ae18f5 --- /dev/null +++ b/packages/core/useSorted/index.test.ts @@ -0,0 +1,79 @@ +import { unref } from 'vue' +import { useSorted } from '.' + +interface User { + name: string + age: number +} + +const arr = [10, 3, 5, 7, 2, 1, 8, 6, 9, 4] +const arrSorted = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + +const objArr: User[] = [ + { + name: 'John', + age: 40, + }, + { + name: 'Jane', + age: 20, + }, + { + name: 'Joe', + age: 30, + }, + { + name: 'Jenny', + age: 22, + }, +] +const objectSorted: User[] = [ + { + name: 'Jane', + age: 20, + }, + { + name: 'Jenny', + age: 22, + }, + { + name: 'Joe', + age: 30, + }, + { + name: 'John', + age: 40, + }, +] + +describe('useSorted', () => { + it('should be defined', () => { + expect(useSorted).toBeDefined() + }) + + it('should sort', () => { + const sorted = useSorted(arr) + expect(unref(sorted)).toMatchObject(arrSorted) + }) + + it('should pure sort function', () => { + const sorted = useSorted(arr) + expect(unref(sorted)).toMatchObject(arrSorted) + }) + + it('should dirty sort', () => { + const dirtyArr = [...arr] + const sorted = useSorted(dirtyArr, { dirty: true }) + + expect(unref(sorted)).toMatchObject(arrSorted) + expect(unref(dirtyArr)).toMatchObject(arrSorted) + }) + + it('should sort object', () => { + const sorted = useSorted(objArr, { + compareFn: (a, b) => a.age - b.age, + }) + + expect(unref(sorted)).toMatchObject(objectSorted) + }) +}) diff --git a/packages/core/useSorted/index.ts b/packages/core/useSorted/index.ts new file mode 100644 index 00000000000..6c3e40c916d --- /dev/null +++ b/packages/core/useSorted/index.ts @@ -0,0 +1,51 @@ +import { computed } from '@vue/reactivity' +import type { MaybeRef } from '@vueuse/shared' +import { isRef, unref, watchEffect } from 'vue-demi' + +export type UseSortedCompareFn = (a: T, b: T) => number + +export type UseSortedFn = (arr: T[], compareFn: UseSortedCompareFn) => T[] + +export interface UseSortedOptions { + /** + * sort algorithm + */ + sortFn?: UseSortedFn + /** + * compare function + */ + compareFn?: UseSortedCompareFn + /** + * change the value of the source array + * @default false + */ + dirty?: boolean +} + +const defaultSortFn = (source: T[], compareFn: UseSortedCompareFn): T[] => source.sort(compareFn) +const defaultCompare: UseSortedCompareFn = (a, b) => a - b + +/** + * reactive sort array + * + * @see https://vueuse.org/useSorted + * @param source source array + * @param options + */ +export function useSorted(source: MaybeRef, options: UseSortedOptions = {}) { + const { sortFn = defaultSortFn, compareFn = defaultCompare, dirty = false } = options + + if (!dirty) + return computed(() => sortFn(unref(source), compareFn as UseSortedCompareFn)) + + // dirty + watchEffect(() => { + const result = sortFn(unref(source), compareFn as UseSortedCompareFn) + if (isRef(source)) + source.value = result + else + source.splice(0, source.length, ...result) + }) + + return source +} From 9318397a3ede541398a8d69a6b0b97f3dbbce42e Mon Sep 17 00:00:00 2001 From: Jelf Date: Fri, 9 Sep 2022 00:45:30 +0800 Subject: [PATCH 2/6] refactor: `compareFn` move to args --- packages/core/useSorted/demo.vue | 4 +--- packages/core/useSorted/index.ts | 9 +++------ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/core/useSorted/demo.vue b/packages/core/useSorted/demo.vue index 346b13f53c1..8b12ce32b5f 100644 --- a/packages/core/useSorted/demo.vue +++ b/packages/core/useSorted/demo.vue @@ -16,9 +16,7 @@ const objArr = [{ name: 'Jenny', age: 22, }] -const result2 = useSorted(objArr, { - compareFn: (a, b) => a.age - b.age, -}) +const result2 = useSorted(objArr, (a, b) => a.age - b.age) const arrText = ref('') const inputArr = computed(() => arrText.value.split(',')) diff --git a/packages/core/useSorted/index.ts b/packages/core/useSorted/index.ts index 6c3e40c916d..bc65dc3cd94 100644 --- a/packages/core/useSorted/index.ts +++ b/packages/core/useSorted/index.ts @@ -11,10 +11,6 @@ export interface UseSortedOptions { * sort algorithm */ sortFn?: UseSortedFn - /** - * compare function - */ - compareFn?: UseSortedCompareFn /** * change the value of the source array * @default false @@ -32,8 +28,9 @@ const defaultCompare: UseSortedCompareFn = (a, b) => a - b * @param source source array * @param options */ -export function useSorted(source: MaybeRef, options: UseSortedOptions = {}) { - const { sortFn = defaultSortFn, compareFn = defaultCompare, dirty = false } = options +// @ts-expect-error default compareFn +export function useSorted(source: MaybeRef, compareFn: UseSortedCompareFn = defaultCompare, options: UseSortedOptions = {}) { + const { sortFn = defaultSortFn, dirty = false } = options if (!dirty) return computed(() => sortFn(unref(source), compareFn as UseSortedCompareFn)) From b52345f8f6bf92f746d692118fe37ed471ee4754 Mon Sep 17 00:00:00 2001 From: Jelf Date: Fri, 9 Sep 2022 00:45:48 +0800 Subject: [PATCH 3/6] doc: update --- packages/core/useSorted/index.md | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/packages/core/useSorted/index.md b/packages/core/useSorted/index.md index 119eb546f82..89330db52c4 100644 --- a/packages/core/useSorted/index.md +++ b/packages/core/useSorted/index.md @@ -1,5 +1,5 @@ --- -category: Sort +category: Array --- # useSorted @@ -12,7 +12,10 @@ reactive sort array import { quickSort, useSorted } from '@vueuse/core' // general sort -const sorted = useSorted([10, 3, 5, 7, 2, 1, 8, 6, 9, 4]) +const source = [10, 3, 5, 7, 2, 1, 8, 6, 9, 4] +const sorted = useSorted(source) +console.log(sorted.value) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +console.log(source) // [10, 3, 5, 7, 2, 1, 8, 6, 9, 4] // object sort const objArr = [{ @@ -28,22 +31,15 @@ const objArr = [{ name: 'Jenny', age: 22, }] -const objSorted = useSorted(objArr, quickSort, { - compareFn: (a, b) => a.age - b.age, -}) +const objSorted = useSorted(objArr, (a, b) => a.age - b.age) ``` +### dirty mode -### useSortedWrapFn - +dirty mode will change the source array. ```ts -const wrapFn = useSortedWrapFn(quickSort, { - compareFn: (a, b) => a.age - b.age, -}) -const sorted = wrapFn(objArr) - -// or -const wrapFn = useSortedWrapFn(quickSort) -const sorted = wrapFn(objArr, { - compareFn: (a, b) => a.age - b.age, +const source = ref([10, 3, 5, 7, 2, 1, 8, 6, 9, 4]) +const sorted = useSorted(source, (a, b) => a - b, { + dirty: true, }) +console.log(source)// output: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ``` From 86bb364a5b00882ec74d1c0d4aa3e334ed6b1f76 Mon Sep 17 00:00:00 2001 From: Jelf Date: Fri, 9 Sep 2022 00:47:54 +0800 Subject: [PATCH 4/6] fix: `Array.prototype.sort` is dirty --- packages/core/useSorted/index.test.ts | 25 ++++++++++++++++--------- packages/core/useSorted/index.ts | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/core/useSorted/index.test.ts b/packages/core/useSorted/index.test.ts index 7ec59ae18f5..f280420baf4 100644 --- a/packages/core/useSorted/index.test.ts +++ b/packages/core/useSorted/index.test.ts @@ -51,28 +51,35 @@ describe('useSorted', () => { expect(useSorted).toBeDefined() }) - it('should sort', () => { - const sorted = useSorted(arr) - expect(unref(sorted)).toMatchObject(arrSorted) - }) - it('should pure sort function', () => { const sorted = useSorted(arr) expect(unref(sorted)).toMatchObject(arrSorted) + expect(unref(arr)).toMatchInlineSnapshot(` + [ + 10, + 3, + 5, + 7, + 2, + 1, + 8, + 6, + 9, + 4, + ] + `) }) it('should dirty sort', () => { const dirtyArr = [...arr] - const sorted = useSorted(dirtyArr, { dirty: true }) + const sorted = useSorted(dirtyArr, (a, b) => a - b, { dirty: true }) expect(unref(sorted)).toMatchObject(arrSorted) expect(unref(dirtyArr)).toMatchObject(arrSorted) }) it('should sort object', () => { - const sorted = useSorted(objArr, { - compareFn: (a, b) => a.age - b.age, - }) + const sorted = useSorted(objArr, (a, b) => a.age - b.age) expect(unref(sorted)).toMatchObject(objectSorted) }) diff --git a/packages/core/useSorted/index.ts b/packages/core/useSorted/index.ts index bc65dc3cd94..d2e3d3052fa 100644 --- a/packages/core/useSorted/index.ts +++ b/packages/core/useSorted/index.ts @@ -33,7 +33,7 @@ export function useSorted(source: MaybeRef, compareFn: UseSortedCo const { sortFn = defaultSortFn, dirty = false } = options if (!dirty) - return computed(() => sortFn(unref(source), compareFn as UseSortedCompareFn)) + return computed(() => sortFn([...unref(source)], compareFn as UseSortedCompareFn)) // dirty watchEffect(() => { From 931213ebba2ded4bc11dac412ccd6d11abed59bf Mon Sep 17 00:00:00 2001 From: Jelf Date: Fri, 9 Sep 2022 01:35:47 +0800 Subject: [PATCH 5/6] feat: optional arguments --- packages/core/useSorted/index.test.ts | 10 +++++- packages/core/useSorted/index.ts | 49 +++++++++++++++++++++------ 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/packages/core/useSorted/index.test.ts b/packages/core/useSorted/index.test.ts index f280420baf4..8683aa9e60f 100644 --- a/packages/core/useSorted/index.test.ts +++ b/packages/core/useSorted/index.test.ts @@ -75,7 +75,7 @@ describe('useSorted', () => { const sorted = useSorted(dirtyArr, (a, b) => a - b, { dirty: true }) expect(unref(sorted)).toMatchObject(arrSorted) - expect(unref(dirtyArr)).toMatchObject(arrSorted) + expect(unref(dirtyArr)).toMatchObject(unref(sorted)) }) it('should sort object', () => { @@ -83,4 +83,12 @@ describe('useSorted', () => { expect(unref(sorted)).toMatchObject(objectSorted) }) + + it('should sort object by options.compareFn', () => { + const sorted = useSorted(objArr, { + compareFn: (a, b) => a.age - b.age, + }) + + expect(unref(sorted)).toMatchObject(objectSorted) + }) }) diff --git a/packages/core/useSorted/index.ts b/packages/core/useSorted/index.ts index d2e3d3052fa..aebffa3a909 100644 --- a/packages/core/useSorted/index.ts +++ b/packages/core/useSorted/index.ts @@ -1,16 +1,20 @@ -import { computed } from '@vue/reactivity' +import type { Ref } from 'vue-demi' import type { MaybeRef } from '@vueuse/shared' -import { isRef, unref, watchEffect } from 'vue-demi' +import { computed, isRef, unref, watchEffect } from 'vue-demi' -export type UseSortedCompareFn = (a: T, b: T) => number +export type UseSortedCompareFn = (a: T, b: T) => number -export type UseSortedFn = (arr: T[], compareFn: UseSortedCompareFn) => T[] +export type UseSortedFn = (arr: T[], compareFn: UseSortedCompareFn) => T[] -export interface UseSortedOptions { +export interface UseSortedOptions { /** * sort algorithm */ sortFn?: UseSortedFn + /** + * compare function + */ + compareFn?: UseSortedCompareFn /** * change the value of the source array * @default false @@ -18,9 +22,12 @@ export interface UseSortedOptions { dirty?: boolean } -const defaultSortFn = (source: T[], compareFn: UseSortedCompareFn): T[] => source.sort(compareFn) +const defaultSortFn: UseSortedFn = (source: T[], compareFn: UseSortedCompareFn): T[] => source.sort(compareFn) const defaultCompare: UseSortedCompareFn = (a, b) => a - b +export function useSorted(source: MaybeRef, compareFn?: UseSortedCompareFn): Ref +export function useSorted(source: MaybeRef, options?: UseSortedOptions): Ref +export function useSorted(source: MaybeRef, compareFn?: UseSortedCompareFn, options?: Omit, 'compareFn'>): Ref /** * reactive sort array * @@ -28,16 +35,36 @@ const defaultCompare: UseSortedCompareFn = (a, b) => a - b * @param source source array * @param options */ -// @ts-expect-error default compareFn -export function useSorted(source: MaybeRef, compareFn: UseSortedCompareFn = defaultCompare, options: UseSortedOptions = {}) { - const { sortFn = defaultSortFn, dirty = false } = options +export function useSorted(...args: any[]) { + const [source] = args + let compareFn: UseSortedCompareFn = defaultCompare + let options: UseSortedOptions = {} + + if (args.length === 2) { + if (typeof args[1] === 'object') { + options = args[1] + compareFn = options.compareFn ?? defaultCompare + } + else { + compareFn = args[1] ?? defaultCompare + } + } + else if (args.length > 2) { + compareFn = args[1] ?? defaultCompare + options = args[2] ?? {} + } + + const { + dirty = false, + sortFn = defaultSortFn, + } = options if (!dirty) - return computed(() => sortFn([...unref(source)], compareFn as UseSortedCompareFn)) + return computed(() => sortFn([...unref(source)], compareFn)) // dirty watchEffect(() => { - const result = sortFn(unref(source), compareFn as UseSortedCompareFn) + const result = sortFn(unref(source), compareFn) if (isRef(source)) source.value = result else From 0e1e56747c19ab7c8dba0a4421ba68f034afea33 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Mon, 17 Oct 2022 06:52:33 +0800 Subject: [PATCH 6/6] Update packages/core/useSorted/index.md --- packages/core/useSorted/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/useSorted/index.md b/packages/core/useSorted/index.md index 89330db52c4..e2df0c3172e 100644 --- a/packages/core/useSorted/index.md +++ b/packages/core/useSorted/index.md @@ -9,7 +9,7 @@ reactive sort array ## Usage ```ts -import { quickSort, useSorted } from '@vueuse/core' +import { useSorted } from '@vueuse/core' // general sort const source = [10, 3, 5, 7, 2, 1, 8, 6, 9, 4]