Skip to content

Commit

Permalink
feat(useArrayIncludes): new function (#2708)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
  • Loading branch information
Alfred-Skyblue and antfu committed Mar 4, 2023
1 parent a2a338d commit 4d6bc00
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/shared/index.ts
Expand Up @@ -37,6 +37,7 @@ export * from './useArrayFilter'
export * from './useArrayFind'
export * from './useArrayFindIndex'
export * from './useArrayFindLast'
export * from './useArrayIncludes'
export * from './useArrayJoin'
export * from './useArrayMap'
export * from './useArrayReduce'
Expand Down
22 changes: 22 additions & 0 deletions packages/shared/useArrayIncludes/index.md
@@ -0,0 +1,22 @@
---
category: Array
---

# useArrayIncludes

Reactive `Array.includes`

## Usage

### Use with reactive array

```js
import { useArrayIncludes } from '@vueuse/core'
const list = ref([0, 2, 4, 6, 8])
const result = useArrayIncludes(list, 10)
// result.value: false
list.value.push(10)
// result.value: true
list.value.pop()
// result.value: false
```
40 changes: 40 additions & 0 deletions packages/shared/useArrayIncludes/index.test.ts
@@ -0,0 +1,40 @@
import { ref } from 'vue-demi'
import { useArrayIncludes } from './index'

describe('useArrayIncludes', () => {
it('should be defined', () => {
expect(useArrayIncludes).toBeDefined()
})

it('should work with array of refs', () => {
const array = ref([0, 2, 4, 6])
const result = useArrayIncludes(array, 8)
expect(result.value).toBeFalsy()
array.value.push(8)
expect(result.value).toBeTruthy()
array.value.pop()
expect(result.value).toBeFalsy()
})

it('should work with array of refs and comparator', () => {
const array = ref([{ id: 1 }, { id: 2 }, { id: 3 }])
const result = useArrayIncludes(array, 3, 'id')
expect(result.value).toBeTruthy()
array.value.pop()
expect(result.value).toBeFalsy()
})

it('should work with array of refs and comparatorFn', () => {
const array = ref([{ id: 1 }, { id: 2 }, { id: 3 }])
const result = useArrayIncludes(array, { id: 3 }, (element, value) => element.id === value.id)
expect(result.value).toBeTruthy()
array.value.pop()
expect(result.value).toBeFalsy()
})

it('should work with array of refs and fromIndex', () => {
const array = ref([{ id: 1 }, { id: 2 }, { id: 3 }])
const result = useArrayIncludes(array, { id: 1 }, { fromIndex: 1, comparator: (element, value) => element.id === value.id })
expect(result.value).toBeFalsy()
})
})
69 changes: 69 additions & 0 deletions packages/shared/useArrayIncludes/index.ts
@@ -0,0 +1,69 @@
import type { ComputedRef } from 'vue-demi'
import { computed } from 'vue-demi'
import { containsProp, isObject } from '../utils'
import type { MaybeComputedRef } from '../utils'
import { resolveUnref } from '../resolveUnref'

export type UseArrayIncludesComparatorFn<T, V> = ((element: T, value: V, index: number, array: MaybeComputedRef<T>[]) => boolean)

function isArrayIncludesOptions<T, V>(obj: any): obj is UseArrayIncludesOptions<T, V> {
return isObject(obj) && containsProp(obj, 'formIndex', 'comparator')
}

export interface UseArrayIncludesOptions<T, V> {
fromIndex?: number
comparator?: UseArrayIncludesComparatorFn<T, V> | keyof T
}

export function useArrayIncludes<T, V = any>(
list: MaybeComputedRef<MaybeComputedRef<T>[]>,
value: MaybeComputedRef<V>,
comparator?: UseArrayIncludesComparatorFn<T, V>,
): ComputedRef<boolean>
export function useArrayIncludes<T, V = any>(
list: MaybeComputedRef<MaybeComputedRef<T>[]>,
value: MaybeComputedRef<V>,
comparator?: keyof T,
): ComputedRef<boolean>
export function useArrayIncludes<T, V = any>(
list: MaybeComputedRef<MaybeComputedRef<T>[]>,
value: MaybeComputedRef<V>,
options?: UseArrayIncludesOptions<T, V>,
): ComputedRef<boolean>
/**
* Reactive `Array.includes`
*
* @see https://vueuse.org/useArrayIncludes
*
* @returns {boolean} true if the `value` is found in the array. Otherwise, false.
* @param args
*/
export function useArrayIncludes<T, V = any>(
...args: any[]
): ComputedRef<boolean> {
const list: MaybeComputedRef<MaybeComputedRef<T>[]> = args[0]
const value: MaybeComputedRef<V> = args[1]

let comparator: UseArrayIncludesComparatorFn<T, V> = args[2]
let formIndex = 0

if (isArrayIncludesOptions(comparator)) {
formIndex = comparator.fromIndex ?? 0
comparator = comparator.comparator!
}

if (typeof comparator === 'string') {
const key = comparator as keyof T
comparator = (element: T, value: V) => element[key] === resolveUnref(value)
}

comparator = comparator ?? ((element: T, value: T) => element === resolveUnref(value))

return computed(() =>
resolveUnref(list)
.slice(formIndex)
.some((element, index, array) =>
comparator(resolveUnref(element), resolveUnref(value), index, resolveUnref(array)),
),
)
}

0 comments on commit 4d6bc00

Please sign in to comment.