This implementation seems to be working fine ( Stackblitz ):
/**
* Returns all the elements that are distinct by the
* `property` value. Note that the implementation uses a `Map<string, E>` to
* index the entities by key. Therefore the more recent occurences
* matching a key instance will overwrite the previous ones.
*
* @param property The name of the property to check for distinct values by.
* @param entities The entities in the array.
*
* @example
* ```
* let todos:Todo = [{ id: 1, "Lets do it!" }, {id: 2, "All done!"}];
* let dtodos:Todo[] = distinct<Todo>(todos, 'id');
*/
export function distinct<E>(entities:E[], property:string):E[] {
let map:Map<string, E> = new Map();
entities.forEach((e:E)=>{
map.set(e[property], e);
});
return Array.from(map.values());
}
The only thing is that VSCode draws a red squiggly under the e[property]
part and the error message is:
Element implicitly has an 'any' type because type '{}' has no index signature.ts(7017)
Is there a way to get rid of that?
I added the latest suggested implementation to this light weight state manager for objects and entities:
https://www.npmjs.com/package/@fireflysemantics/slice
npm i @fireflysemantics/slice
...
import {distinct} from '@fireflysemantics/slice/utilities';
The Error message is a bit misleading. His problem is, that it can not ensure that e[property]
is of type string
as you've defined the Map
.
Make the key in the Map of type any
as with so much flexibility you can not determine the type of the value either.
Additionally I'd type the property
parameter as keyof E
so TS ensures that I can only paste in valid property names for the type.
function distinct<E>(entities:E[], property:keyof E):E[] {
let map:Map<any, E> = new Map();
entities.forEach((e:E)=>{
map.set(e[property], e);
});
return Array.from(map.values());
}
Based on Thomas ' answer, we can simplify both:
Map
at once; K extends keyof E
, we can cast the tuples ( [E[K], E]
) used as Map
constructor input parameter and remove the any
type use. Here the code:
function distinct<E, K extends keyof E>(entities: E[], property: K): E[] {
const entitiesByProperty = new Map(entities.map(e => [e[property], e] as [E[K], E]));
return Array.from(entitiesByProperty.values());
}
When calling distinct()
, there's no need to specify the generic types since they can be inferred. Here's a working example:
enum Status { Pending = 0, Done = 1 }
interface Todo { id: number, label: string, status: Status, date?: Date }
const todos: Todo[] = [
{ id: 1, label: 'Task 1', status: Status.Pending },
{ id: 2, label: 'Task 2', status: Status.Pending },
{ id: 1, label: 'Task 1', status: Status.Done },
];
distinct(todos, 'id'); // [{ id: 1, ... status: 1 }, { id: 2, ... status: 0 }]
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.