Gathering detailed insights and metrics for indexed-collection
Gathering detailed insights and metrics for indexed-collection
Gathering detailed insights and metrics for indexed-collection
Gathering detailed insights and metrics for indexed-collection
cellx-indexed-collections
Индексируемые коллекции для [cellx](https://github.com/Riim/cellx).
@_greataxe/indexed-object
A TypeScript utility for working with indexed collections of objects
@nano-sql/core
Like Lego For Databases
use-item-list
Manage indexed collections in React using hooks.
A zero-dependency library of classes that make filtering, sorting and observing changes to arrays easier and more efficient.
npm install indexed-collection
Typescript
Module System
Min. Node Version
Node Version
NPM Version
TypeScript (96.29%)
JavaScript (3.57%)
Shell (0.13%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
4 Stars
42 Commits
1 Watchers
5 Branches
1 Contributors
Updated on Jul 11, 2025
Latest Version
3.1.0
Package Id
indexed-collection@3.1.0
Unpacked Size
108.46 kB
Size
22.79 kB
File Count
41
NPM Version
10.9.2
Node Version
22.17.0
Published on
Jul 09, 2025
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
A zero-dependency library of classes that make filtering, sorting and observing changes to arrays easier and more efficient.
Installation
1#npm 2npm install indexed-collection 3 4# yarn 5yarn add indexed-collection
The following contrived example that walks through some basic features of Collect, CollectionView and Index.
Assuming we have a community of people that consists of the following fields
The community want to keep statistics by gender initially for everybody. Let's build a collection that indexes on gender
1import { IndexedCollectionBase, CollectionIndex, CollectionViewBase } from 'indexed-collection'; 2 3class PeopleCollection extends IndexedCollectionBase { 4 constructor() { 5 super(); 6 this.genderIndex = new CollectionIndex([(person) => person.gender]); 7 this.buildIndexes([ 8 this.genderIndex, 9 ]); 10 } 11 12 byGender(gender) { 13 return this.genderIndex.getValue(gender); 14 } 15}
Next, let's add some people
1 2const people = new PeopleCollection(); 3people.add({ name: 'John', gender: 'male', age: 70, hobbies: ['fishing', 'hiking'] }); 4people.add({ name: 'Mary', gender: 'female', age: 50, hobbies: ['hiking', 'bowling'] }); 5people.add({ name: 'Chris', gender: 'male', age: 35, hobbies: ['hiking', 'kayaking'] }); 6people.add({ name: 'Ana', gender: 'femal', age: 32, hobbies: ['bowling', 'kayaking'] }); 7 8// let's report 9people.count; // 4 10people.items; // [John, Mary, Chris, Ana] 11people.byGender('male'); // [John, Chris] 12people.byGender('female'); // [Mary, Ana]
Besides tracking all the people in the community, the community also want to track all the seniors (age 65 or above). Given
this collection is a subset of the PeopleCollect
, we can utilize the CollectionView, which is a readonly collection that
derives values from a base collection.
1
2class SeniorCollection {
3 constructor(baseCollection) {
4 super(baseCollection, {
5 filter: (person) => person.age >= 65
6 })
7 }
8
9 byGender(gender) {
10 return super.applyFilterAndSort(
11 this.source.byGender(gender)
12 );
13 }
14}
15
16
17const seniors = new SeniorCollection(people);
18seniors.count; // 1
19seniors.byGender('male'); // [John]
20seniors.byGender('female'); // [] No-one
21
The beauty of Collection and CollectionView is it can handle data changes. For example, let's add a new people to the community
1people.add({ name: 'Betty', age: 68, gender: 'female', hobbies: ['fishing'] }); 2 3people.count // 5 4people.byGender('female'); // Mary, Ana, Betty 5 6// The collection view is also updated because it stays in sync with its base collection 7seniors.count // 2 8seniors.byGender('female'); // [Betty]
The community want to report on people's hobbies, and also want to report on hobbies & gender as well.
To do so, we need to add two indexes, one for hobbies, and one for both hobbies and gender.
1
2// Revise within PeopleCollection's constructor
3const getHobby = (person) => person.hobbies;
4getHobby.isMultiple = true; // isMultiple = true indicates the value extracted is an array or a set of values
5
6this.hobbyIndex = new CollectionIndex([getBobby]);
7this.genderAndHobbyIndex = new CollectionIndex( [person => person.gender, getHobby] );
8
9this.buildIndexes([
10 this.genderIndex,
11 this.hobbyIndex,
12 this.genderAndHobbyIndex,
13]);
1// Add byHobby and byGenderAndHobby methods to PeopleCollection for ease of access
2byHobby(hobby) {
3 return this.hobbyIndex.getValue(hobby);
4}
5
6byGenderAndHobby(gender, hobby) {
7 return this.hobbyIndex.getValue(gender, hobby);
8}
1people.byHobby('fishing'); // [John, Betty] 2people.byHooby('hiking'); // [John, Mary, Chris] 3people.byGenderAndHobby('male', 'hiking'); // [John, Chris]
Next, let's propagate the byHobby and byGenderAndHobby to the collection SeniorCollection class
1byHobby(hobby) {
2 return super.applyFilterAndSort(
3 this.source.byHobby(hobby)
4 );
5}
6
7byGenderAndHobby(gender, hobby) {
8 return super.applyFilterAndSort(
9 this.source.byGenderAndHobby(gender, hobby)
10 );
11}
1seniors.byHobby('finshing'); // [John, Betty] 2seniors.byGenderAndHobby('hiking'); // [John]
Indexed collection consists of three key parts: index, collection, and view.
Index defines how a collection should be indexed for each retrieval. A common use case of index would be indexing on a field of each line item, thus the index would group items by the value of the field. To create an index, use the CollectionIndex class.
For example
1// JavaScript 2const byGenderIndex = new CollectionIndex( [ (person) => person.gender ]); 3 4// TypeScript 5const byGenderIndex: <IPerson, [string]> = new CollectionIndex( [ (person) => person.gender ]);
CollectionIndex
supports multiple level of indexes. Multiple level
indexes are useful when values need to be further grouped into levels of subgroups.
For example, if we wish to group people by gender then by age, the index can look like
1// JavaScript 2const byGenderAndAgeIndex = new CollectionIndex( [ (person) => person.gender, (person) => person.age ]); 3 4// TypeScript 5const byGenderAndAgeIndex: <IPerson, [string, number]> = new CollectionIndex( [ (person) => person.gender, (person) => person.age ]);
Sometimes, and field in each item may consist of an array or set of values, we can annotate
the index with isMultiple=true
, so each value of the field become a key in the index, thus it
creates a many-to-many relationship.
For example, in the example above, each person has multiple hobbies, and multiple people
can have the same hobby, therefore hobby and person has many-to-many relationship, so to index
the hobby, isMultiple=true
is needed.
1// JavaScript 2const getHobbies = (person) => person.hobbies; 3getHobbies.isMultiple = true; 4const byHobbyIndex = new CollectionIndex( getHobbies ); 5 6// TypeScript 7const getHobbies: MultipleKeyExtract<IPerson, string> = (person) => person.hobbies; 8getHobbies.isMultiple = true; 9const byHobbyIndex = new CollectionIndex<IPerson, [string]>( getHobbies );
Value of index is not limited to number or string, it can be anything in JavaScript. Keep in mind value comparison is index is strict equal.
Additionally, one can also use index to do advanced indexing such as value bucketing, for example people can be indexed/grouped by age range (10-19, 20-19, ... etc), please see Advanced Topics for more details.
Collection provides way to add, remove and retrieve items. Each collection can consist of zero to many indexes. Underneath the hood, the collection would orchestrate all the indexes when items are added or removed.
To create a collection, one would need to create a class that extends either IndexedCollectionBase
or PrimaryKeyCollection
. IndexedCollectionBase
identifies duplicates by performing
strict equality of each item added to the collection; PrimaryKeyCollection
identifies duplicates by
performing strict equality of each item's primary key value such as the ID value of an item.
A typical Collection class would consist of the following elements
For example, the PeopleCollection class in the example provides a typical JavaScript example, a TypeScript equivalent would be as the following,
1import { CollectionIndex } from './CollectionIndex'; 2import { MultipleKeyExtract } from './KeyExtract'; 3import { IndexedCollectionBase } from './IndexedCollectionBase'; 4 5class PeopleCollection extends IndexedCollectionBase<IPerson> { 6 private readonly byGenderIndex: CollectionIndex<IPerson, [string]>; 7 private readonly byHobbyIndex: CollectionIndex<IPerson, [string]>; 8 private readonly byGenderAndHobbyIndex: CollectionIndex< 9 IPerson, 10 [string, string] 11 >; 12 13 constructor(initialValues?: readonly IPerson[]) { 14 super(); 15 16 const getGender = (person: IPerson) => person.gender; 17 const getHobbies: MultipleKeyExtract<IPeson, string> = (person: IPerson) => 18 person.hobbies; 19 getHobbies.isMultiple = true; 20 21 this.byGenderIndex = new CollectionIndex<IPerson, [string]>([getGender]); 22 this.byHobbyIndex = new CollectionIndex<IPerson, [string]>([getHobbies]); 23 this.byGenderAndHobbyIndex = new CollectionIndex<IPerson, [string]>([ 24 getGender, 25 getHobbies, 26 ]); 27 28 this.buildIndexes([ 29 this.byGenderIndex, 30 this.byHobbyIndex, 31 this.byGenderAndHobbyIndex, 32 ]); 33 34 if (initialValues) { 35 this.addRange(initialValues); 36 } 37 } 38 39 // Helper methods to help extract values from indexes 40 byGender(gender: string): readonly IPerson[] { 41 return this.byGenderIndex.getValues(gender); 42 } 43 44 byHobby(hobby: string): readonly IPerson[] { 45 return this.byHobbyIndex.getValues(gender); 46 } 47 48 byGenderAndHobby(gender: string, hobby: string): readonly IPerson[] { 49 return this.byGenderAndHobbyIndex.getValues(gender, hobby); 50 } 51}
To create collection based on PrimaryKeyCollection, a function that extracts the id from each item would need to be provided in the constructor. For example
1// Assuming each IPerson is identified by a unique SSN which is a number 2class PeopleCollection extends PrimaryKeyCollection<IPerson, number> { 3 constructor(initialValues?: readonly IPerson[]) { 4 super((person: IPerson) => person.ssn); 5 6 // Additional indexes similar to IndexedCollectionBased example above 7 } 8}
Collection also support other features such as change observation etc, please see Advanced Topics for more details.
If a Collection is seen as a table in a relational database, a CollectionView is similar to View in the relational database as well. A view is a read-only version of a collection with values filtered and sorted.
A view has to depend on a source collection or CollectionView, and any changes on sourced collection would immediately impact the views output.
To define a view, one would need to create a class based on CollectionViewBase
class with the following
elements:
this.applyFilterandSort( parent.helperMethod )
For example
1class SeniorPeopleView extends CollectionViewBase<IPerson, PeopleCollection> { 2 constructor(source: PeopleCollection) { 3 super(source, { 4 filter: (person: IPerson) => person.age >= 65, 5 6 // Always sort by age in ascending order 7 sort: (a: IPerson, b: IPerson) => a.age - b.age, 8 }); 9 } 10 11 byGender(gender: string): readonly IPerson[] { 12 return this.applyFilterAndSort(this.source.byGender(gender)); 13 } 14 15 // byHobby, byGenderAndHobby are similar to byGender 16}
A collection view can be nested from another view. This can be used for representing further
data subset. For example, SeniorFemalePeopleView
can be sourced from SeniorPeopleView
, for example
1class SeniorPeopleView extends CollectionViewBase<IPerson, SeniorPeopleView> { 2 constructor(source: SeniorPeopleView) { 3 super(source, { 4 // Note that filter does not need to define the age constrain 5 filter: (person: IPerson) => person.gender === 'female', 6 }); 7 } 8 9 byGender(gender: string): readonly IPerson[] { 10 return this.applyFilterAndSort(this.source.byGender(gender)); 11 } 12 13 // byHobby, byGenderAndHobby are similar to byGender 14}
Note that filter is nested when a view is sourced from another view. However, sorting is not nested, each view has to manage its own sort order. If sort is undefined, the view would inherit the natural order from its source.
Coming soon.
No vulnerabilities found.
No security vulnerabilities found.