diff --git a/docs/docs/features/entities-management/entities.mdx b/docs/docs/features/entities-management/entities.mdx index 89e29626..8fa0dc38 100644 --- a/docs/docs/features/entities-management/entities.mdx +++ b/docs/docs/features/entities-management/entities.mdx @@ -239,6 +239,18 @@ import { getEntityByPredicate } from '@ngneat/elf-entities'; const todo = todosStore.query(getEntityByPredicate(({ title }) => title === 'Elf')); ``` +### `getManyByPredicate` + +Get multiple entities from the store by predicate: + +```ts +import { getManyByPredicate } from '@ngneat/elf-entities'; + +const todo = todosStore.query(getManyByPredicate(({ active }) => active)); +const todo = todosStore.query(getManyByPredicate(({ active }) => active), { pluck: 'title }); +const todo = todosStore.query(getManyByPredicate(({ active }) => active), { ref: UIEntitiesRef, pluck: 'title }); +``` + ### `hasEntity` Returns whether an entity exists: diff --git a/packages/entities/src/lib/queries.spec.ts b/packages/entities/src/lib/queries.spec.ts index 385017f3..e227330a 100644 --- a/packages/entities/src/lib/queries.spec.ts +++ b/packages/entities/src/lib/queries.spec.ts @@ -12,6 +12,7 @@ import { hasEntity, getEntitiesIds, getEntityByPredicate, + getManyByPredicate, } from './queries'; import { addEntities } from './add.mutation'; import { UIEntitiesRef } from './entity.state'; @@ -195,4 +196,95 @@ describe('queries', () => { ] `); }); + + describe('getManyByPredicate', () => { + it('should return the collection', () => { + const store = createEntitiesStore(); + store.update(addEntities(createTodo(1))); + store.update(addEntities(createTodo(2))); + store.update(addEntities(createTodo(3))); + store.update(addEntities(createTodo(4))); + expect( + store.query(getManyByPredicate((el: Todo) => [2, 3].includes(el.id))) + ).toMatchInlineSnapshot(` + Array [ + Object { + "completed": false, + "id": 2, + "title": "todo 2", + }, + Object { + "completed": false, + "id": 3, + "title": "todo 3", + }, + ] + `); + }); + it('should work with pluck', () => { + const store = createEntitiesStore(); + store.update(addEntities(createTodo(1))); + store.update(addEntities(createTodo(2))); + store.update(addEntities(createTodo(3))); + store.update(addEntities(createTodo(4))); + expect( + store.query(getManyByPredicate((el: Todo) => [4, 3].includes(el.id))) + ).toMatchInlineSnapshot(` + Array [ + 3, + 4, + ] + `); + }); + + it('should work with ref', () => { + const store = createUIEntityStore(); + + store.update(addEntities(createUITodo(1), { ref: UIEntitiesRef })); + store.update(addEntities(createUITodo(2), { ref: UIEntitiesRef })); + store.update(addEntities(createUITodo(3), { ref: UIEntitiesRef })); + store.update(addEntities(createUITodo(4), { ref: UIEntitiesRef })); + expect( + store.query( + getManyByPredicate( + (el: { id: number; open: boolean }) => [2, 3].includes(el.id), + { ref: UIEntitiesRef } + ) + ) + ).toMatchInlineSnapshot(` + Array [ + Object { + "id": 2, + "open": false, + }, + Object { + "id": 3, + "open": false, + }, + ] + `); + }); + + it('should work with ref and pluck', () => { + const store = createUIEntityStore(); + + store.update(addEntities(createUITodo(1), { ref: UIEntitiesRef })); + store.update(addEntities(createUITodo(2), { ref: UIEntitiesRef })); + store.update(addEntities(createUITodo(3), { ref: UIEntitiesRef })); + store.update(addEntities(createUITodo(4), { ref: UIEntitiesRef })); + expect( + store.query( + getManyByPredicate( + (el: { id: number; open: boolean }) => [2, 3].includes(el.id), + { ref: UIEntitiesRef, pluck: 'id' } + ) + ) + ).toMatchInlineSnapshot(` + Array [ + 2, + 3, + ] + `); + }); + }); }); diff --git a/packages/entities/src/lib/queries.ts b/packages/entities/src/lib/queries.ts index c89f6a33..73e6de87 100644 --- a/packages/entities/src/lib/queries.ts +++ b/packages/entities/src/lib/queries.ts @@ -9,6 +9,7 @@ import { ItemPredicate, } from './entity.state'; import { Query } from '@ngneat/elf'; +import { checkPluck } from './entity.utils'; /** * @@ -109,7 +110,7 @@ export function getEntityByPredicate< predicate: ItemPredicate>, options: BaseEntityOptions = {} ): Query | undefined> { - return function (state) { + return function (state) { const { ref: { entitiesKey, idsKey } = defaultEntitiesRef } = options; const entities = state[entitiesKey]; const id = state[idsKey].find((id: getIdType) => { @@ -120,6 +121,44 @@ export function getEntityByPredicate< }; } +/** + * + * Get many entities by predicate + * + * @example + * + * store.query(geManyByPredicate(({ active }) => active)) + * store.query(geManyByPredicate((el: Todo) => el.active, { pluck: 'title' })) + * store.query(geManyByPredicate((el: Todo) => el.active, { ref: UIEntitiesRef, pluck: 'title' })) + */ +export function getManyByPredicate< + S extends EntitiesState, + R extends getEntityType[], + K extends keyof getEntityType, + Ref extends EntitiesRef = DefaultEntitiesRef +>( + predicate: ItemPredicate>, + options?: { + pluck?: K | ((entity: getEntityType) => R); + } & BaseEntityOptions +): Query[]> { + return function (state) { + const { ref: { entitiesKey, idsKey } = defaultEntitiesRef, pluck } = + options || {}; + const filteredEntities: getEntityType[] = []; + + state[idsKey].forEach((id: getIdType, index: number) => { + const entity = state[entitiesKey][id]; + + if (predicate(entity, index)) { + filteredEntities.push(checkPluck(entity, pluck)); + } + }); + + return filteredEntities; + }; +} + /** * * Check whether the entity exist