Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(useArrayIncludes): new function #2708

Merged
merged 12 commits into from
Mar 4, 2023
Merged
1 change: 1 addition & 0 deletions packages/shared/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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)),
),
)
}