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';
https://stackblitz.com/edit/typescript-slice-distinct
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 }]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With