zrwusa / data-structure-typed

Javascript Data Structure & TypeScript Data Structure. Heap, Binary Tree, Red Black Tree, Linked List, Deque, Trie, HashMap, Directed Graph, Undirected Graph, Binary Search Tree, AVL Tree, Priority Queue, Graph, Queue, Tree Multiset, Singly Linked List, Doubly Linked List, Max Heap, Max Priority Queue, Min Heap, Min Priority Queue, Stack.
https://data-structure-typed-docs.vercel.app
MIT License
114 stars 8 forks source link

an optional mappingFn to import arrays of objects #66

Closed simoami closed 8 months ago

simoami commented 8 months ago

Is your feature request related to a problem? Please describe. a common pattern with Maps in general is wanting to index an array of objects by a particular attribute key of the object.

This pattern often looks like this:

new Map(originalArray.map(element => ([element.id, element])))

With the approach above, we would end up with dual iterations over the array because you also call this.setMany(elements)

Describe the solution you'd like An ideal option is to add an optional mappingFn that lets the user customize how to transform the array type to an array to of [key, value]

I had initially thought about adding my own MapUtils.fromArray(), but if it's supported by your HashMap, we would just use yours without additional wrappers. Although untested. it could look like the following:


export type HashMapOptions<K, V, T> = Partial<{
  hashFn: (key: K) => string;
  mappingFn: (element: T) => [K, V];
}>;

export class HashMap<K = unknown, V = unknown, T = [K, V]> {
  constructor(entries: Iterable<T> = [], options?: HashMapOptions<K, V, T>) {
    super();
    const { hashFn, mappingFn } = options || {};
    if (hashFn) {
      this._hashFn = hashFn;
    }
    if (entries) {
      this.setMany(entries, mappingFn);
    }
  }

  setMany(
    entries: Iterable<T>,
    mappingFn?: HashMapOptions<K, V, T>['mappingFn'],
  ): Array<boolean> {
    const results: Array<boolean> = [];
    for (const entry of entries) {
      const [key, value] = mappingFn ? mappingFn(entry) : entry;
      results.push(this.set(key, value));
    }
    return results;
  }

Usage would be:

const myArray = [
  { id: 1, name: 'item 1' },
  { id: 2, name: 'item 2' },
];
type OriginaArrayType = typeof myArray[number];
const myMap = new HashMap<number, OriginaArrayType, OriginaArrayType>(myArray, {
  mappingFn: (entry) => [entry.id, entry],
});

What's your thoughts on this?

zrwusa commented 8 months ago

This is a great suggestion, making it convenient for raw data conversion while also avoiding performance loss. We have made corresponding modifications based on your suggestion, and this feature is supported in version 1.49.9.

simoami commented 8 months ago

Amazing! thank you. The type inference works better too compared to my example.

const myArray = [
  { id: 1, name: 'item 1' },
  { id: 2, name: 'item 2' },
];

const myMap1 = new HashMap(myArray, {
  toEntryFn: (entry) => [entry.id, entry],
});
zrwusa commented 8 months ago

Glad you're satisfied with our development. Remember to star our project. If you're willing, you can also contribute code. Here's our developer Telegram community: https://t.me/+wlFkdjeFoVpiY2M1.