Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/yoerinijs/pincet
A plain simple - but still very powerful - array utility that is written in Typescript.
https://github.com/yoerinijs/pincet
Last synced: about 17 hours ago
JSON representation
A plain simple - but still very powerful - array utility that is written in Typescript.
- Host: GitHub
- URL: https://github.com/yoerinijs/pincet
- Owner: YoeriNijs
- License: gpl-3.0
- Created: 2020-10-05T19:17:39.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2021-05-07T07:59:12.000Z (over 3 years ago)
- Last Synced: 2024-11-08T08:25:57.098Z (about 2 months ago)
- Language: TypeScript
- Homepage:
- Size: 272 KB
- Stars: 0
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Pincet: for precise array operations only
## What it does
Pincet (Dutch for 'tweezers') is a plain simple - but still very powerful - array utility that is written in Typescript and, therefore, type safe.## Install
`npm i pincet`## Usage
First, call Pincet in your code:
```
import * as pincet from 'pincet';
```### Find methods
Pincet supports some plain simple methods, such as:
```
const arr = ['one', 'two', 'three'];
const first = pincet.findFirst(arr);
console.log(first); // 'one'
```However, you can use Pincet for nested arrays as well. For instance:
```
const arr = [[], [[]], ['one'], 'two', 'three'];
const first = pincet.findFirst(arr);
console.log(first); // 'one'
```You can also search for more values, if you prefer:
```
const arr = [[], ['one'], ['two']];
const first = pincet.findFirstNumber(arr, 2);
console.log(first); // ['one', 'two']
```Or revert it:
```
const arr = [[], ['one'], ['two']];
const last = pincet.findLastNumber(arr, 2);
console.log(last); // ['two', 'one']
```You can also find values with a predicate:
```
interface Person {
name: string,
age: number
}const people: Person[][] = [
[
{
name: 'ChildA',
age: 10
},
{
name: 'ChildB',
age: 8
},
],
[
{
name: 'AdultA',
age: 40
},
{
name: 'AdultB',
age: 20
},
{
name: 'AdultC',
age: 30
},
{
name: 'AdultD',
age: 20
}
]
];const predicate = (person: Person) => person.age === 30;
const result = pincet.findWithPredicate(people, predicate);
console.log(result); // [{ name: 'AdultC', age: 30 }]
```If you like syntactic sugar, you can use `pincet.findAny(people, predicate)` as well.
### Check for values
You can also check if a (nested) array contains a value:
```
const arr = ['do', ['re'], 'mi'];
const result = pincet.contains(arr, 're');
console.log(result); // true
```Optionally, you can pass multiple values. If one of the values is in the array, the contains method returns true:
```
const arr = ['do', 're', 'mi', 'fa'];
const result = pincet.contains(arr, 'so', 'la', 'ti', 'do');
console.log(result); // true
```If you want to check if multiple values exist in an array, you can use `pincet.containsAll()`:
```
const arr = ['aap', 'noot', 'mies', 'wim'];
const result = pincet.containsAll(arr, 'mies', 'wim');
console.log(result); // true
```Hence:
```
const arr = ['aap', 'noot', 'mies'];
const result = pincet.containsAll(arr, 'mies', 'wim');
console.log(result); // false
```You can also verify whether an array contains a partial value. This works for nested arrays as well. Example:
```
interface TestObject {
value: string;
isString: boolean;
}const values: TestObject[] = [
{ value: 'aap', isString: true },
{ value: 'noot', isString: true }
];
const result = pincet.containsPartial(values, { value: 'aap' });
console.log(result); // true
```Or:
```
const values: TestObject[] = [
{ value: 'aap', isString: true },
{ value: 'noot', isString: true }
];
const result = pincet.containsPartial(values, { value: 'mies' });
console.log(result); // false
```### Distinct values
```
const arr = [0, 1, 1, 2, 3, 3];
const result = pincet.unique(arr);
console.log(result); // [0, 1, 2, 3]
```You can specify a depth as well:
```
const arr1 = [0, 0, 1, [1], 2, 3, [3]];
const result1 = pincet.unique(arr1);
console.log(result1); // [0, 1, [1], 2, 3, [3]]const arr2 = [0, 0, 1, [1], 2, 3, [3]];
const result2 = pincet.unique(arr2, 1);
console.log(result2); // [0, 1, 2, 3]
```### Split values
Interested in more results? Just split it:```
interface Valid {
valid: true;
}interface Invalid {
valid: false;
}const arr: any[] = [
{
id: '1',
valid: true
},
{
id: '2',
valid: false
},
{
id: '3',
valid: true
},
];const predicate = (value: any) => value.valid;
const [valid, invalid] = pincet.splitByPredicate(arr, predicate);
console.log(valid); // [{ id: '1', valid: true }, { id: '3', valid: true }];
console.log(invalid); // [{ id: '2', valid: false }];
```You can also revert it:
```
const predicate = (value: any) => !value.valid;
const [invalid, valid] = pincet.splitByPredicate(arr, predicate);
```### Union values
Do you want to union some arrays? There are some Pincet functions for that!```
const first: string[] = ['aap', 'noot'];
const second: string[] = ['mies'];
const result = union(first, second);
console.log(result); // ['aap', 'noot', 'mies']
```Or with predicate:
```
interface Person {
name: string;
age: number
}const first: Person[] = [
{ name: 'Harry Potter', age: 18 }
];
const second: Person[] = [
{ name: 'Severus Snape', age: 38 },
{ name: 'Ronald Weasley', age: 18 }
];
const result = unionWith((p: Person) => p.age < 38, first, second);
console.log(result); // [{ name: 'Harry Potter', age: 18 }, { name: 'Ronald Weasley', age: 18 }]
```You can also flatten multidimensional arrays as once. For this, just use `flatUnion` or `flatUnionWith`.
These methods do exactly the same as `union` and `unionWith`, but with the key difference that nested arrays are
flattened first. The depth of nested arrays is infinite.### Equality checking
Interested in equality? There is a method for that!
```
const arr1 = ['aap', ['noot', ['mies']]];
const arr2 = ['aap', ['noot', ['mies']]];
const result = pincet.isEqual(arr1, arr2);
console.log(result); // true
``````
const arr1 = ['aap', 'noot', 'mies'];
const arr2 = ['aap', 'noot', 'mies'];
const arr3 = ['aap', 'noot', 'wim'];
const arr4 = ['aap', 'noot', 'mies'];
const result = pincet.isEqual(arr1, arr2, arr3, arr4);
console.log(result); // false
```### Map values
```
const arr = [0, 1, 2];
const fn = (v: number) => `${v + 1}`;
const result = pincet.map(arr, fn);
console.log(result); // ['1', '2', '3']
```By default, the map method does not flatten you array. If you want to flat map, you can!
```
const arr = [1, [2, [3]]];
const fn = (v: number) => v + 1;
const result = pincet.flatMap(arr, fn);
console.log(result); // [2, 3, 4]
```You can specify a depth, if you prefer. Here, we just pass a depth of 1:
```
const arr = [['aap', ['noot', ['mies']]]];
const fn = (v: string | string[]) => Array.isArray(v) ? v : 'wim';
const result = pincet.flatMap(arr, fn, 1);
console.log(result); // ['wim', ['noot', ['mies']]]
```If you want, Pincet supports mapping and converting values at once via the convertMap function:
```interface Person {
name: string,
age: number
}const arr: Person[][] = [
[
{ name: 'personA', age: 30 },
{ name: 'personB', age: 31 },
{ name: 'personC', age: 32 }
],
[
{ name: 'personD', age: 40 },
{ name: 'personE', age: 41 },
{ name: 'personF', age: 42 }
]
];const result = pincet.convertMap(arr, 'age');
console.log(result); // [[30, 31, 32], [40, 41, 42]]
```### Replace values
Sometimes, you want to replace some values. Well, now you can with ease!
```
interface Person {
name: string;
gender: string;
}const persons: Person[] = [
{ name: 'Bradley Edward Manning', gender: 'man' },
{ name: 'Nikkie de Jager', gender: 'man' }
];const result = pincet.replaceAll(persons, { gender: 'woman' });
console.log(result); // [ { name: 'Bradley Edward Manning', gender: 'woman' }, { name: 'Nikkie de Jager', gender: 'woman' } ]
```Not interested in replacing all values? Just pass a predicate:
```
interface Person {
name: string;
gender: string;
}const persons: Person[] = [
{ name: 'Bradley Edward Manning', gender: 'man' },
{ name: 'Nikkie de Jager', gender: 'man' }
];
const predicate = (person: Person) => person.name === 'Nikkie de Jager';const result = pincet.replaceAllWithPredicate(persons, { gender: 'woman' }, predicate);
console.log(result); // [ { name: 'Bradley Edward Manning', gender: 'man' }, { name: 'Nikkie de Jager', gender: 'woman' } ]
```Pincet supports replacing one value (optionally with predicate), if needed. Technically this is not array related, but may still be handy!
```
const person: Person = { name: 'Bradley Edward Manning', gender: 'man' };
const result = pincet.replace(person, { gender: 'woman' });
console.log(result); // { name: 'Bradley Edward Manning', gender: 'woman' }
```### Remove values
With Pincet, it is easy to remove a value from a (nested) array:```
const arr = [0, 1, 2, 3];
const result = pincet.remove(arr, 2);
console.log(result); // [0, 1, 3]
```By default, Pincet respects nested arrays. Only when the removal operations leads to an empty array, the array will be filtered out:
```
const arr1 = [0, 1, [2], 3];
const result1 = pincet.remove(arr1, 2);
console.log(result1); // [0, 1, 3]const arr2 = [0, 1, [[2], 3]];
const result2 = pincet.remove(arr2, 2);
console.log(result2); // [0, 1, [3]]
```If you want, you can remove a value and flatten the array directly by using flatRemove:
```
const arr = [0, 1, [[2], 3]];
const result = pincet.flatRemove(arr, 2);
console.log(result); // [0, 1, 3]
```### Count values
```
const arr = ['one', ['two', ['three', 'four'], 'five']];
const result = pincet.count(arr);
console.log(result); // 5
```You can specify a depth:
```
const arr1 = ['one', ['two', ['three', 'four'], 'five']];
const result1 = pincet.count(arr1, 1);
console.log(result1); // 4const arr2 = ['one', ['two', ['three', 'four'], 'five']];
const result2 = pincet.count(arr2, 0);
console.log(result2); // 2
```Or just check for empty arrays:
```
const arr1: any[] = [[], []];
const result1 = pincet.isEmpty(arr1);
console.log(result1); // trueconst arr2: any[] = [[], ['value']];
const result2 = pincet.isEmpty(arr2);
console.log(result2); // false
```There is also a Count decorator. Please see 'supported decorators' in this readme.
### Sort values
Pincet provides a default sorting mechanism. By default, there are methods to sort on strings or numbers. For instance:```
const arr = ['z', 'y', 'x'];
const result = pincet.sort(arr, byStringAsc);
console.log(result); // ['x', 'y', 'z']
```By default, Pincet does not flatten the array. If you want to, you can specify a depth:
```
const arr = [0, 1, [2]];
const result = pincet.sort(arr, byNumberDesc, 1);
console.log(result); // [2, 1, 0]
```At the moment, Pincet provided the following sorters by default:
- byStringAsc
- byStringDesc
- byNumberAsc
- byNumberDescYou can, however, create your own sorter by extending the Sorter interface. The interface is just a type safe wrapper
and holds a sorting function, that returns a number (just as the default comparator). Your implementation may look like this:```
const myCustomSorter: Sorter = { sort: (a, b) => a === b ? 1 : -1 };
```You then may call it like:
```
const arr = [false, false, true, false, true];
const result = pincet.sort(arr, myCustomSorter);
console.log(result); // [true, true, false, false, false]
```Of course, Pincet supports nested arrays as well. Just pass a depth:
```
const arr = [false, [false], true, [false, [true]]];
const result = pincet.sort(arr, myCustomSorter, 2);
console.log(result); // [true, true, false, false, false]
```### Flatten
Wait, one more thing: flatten to the rescue! By default all arrays are flatten.
```
const arr = ['aap', ['noot'], [[['mies']]]];
const result = pincet.flatten(arr);
console.log(result); // ['aap', 'noot', 'mies']
```But you can specify a depth. For example:
```
const arr = ['aap', ['noot'], [[['mies']]]];
const result = pincet.flatten(arr, 1);
console.log(result); // ['aap', 'noot', [['mies']]]
```## Supported methods
##### Find
- `findFirst(values: any[]): T`
- `findFirstNumber(values: any[], nValues: number): T[]`
- `findLast(values: any[]): T`
- `findLastNumber(values: any[], nValues: number): T[]`
- `findWithPredicate(values: T[], predicate: (value: T) => boolean): T[]`
- `findAny(values: T[], guard: (value: T) => boolean): T[]`
- `contains(values: unknown[], ...value: T): boolean`
- `containsAll(values: unknown[], ...value: T[]): boolean`
- `containsPartial(values: object[], expectedPartial: Partial): boolean`##### Split
- `splitByPredicate(values: any[], predicate: (value: S) => boolean): [T1[], T2[]]`##### Union
- `union(...list: T[][]): T[]`
- `unionWith(predicate: (v: T) => boolean, ...list: T[][]): T[]`
- `flatUnion(...list: unknown[][]): unknown[]`
- `flatUnionWith(predicate: (v: any) => boolean, ...list: unknown[]): unknown[]`##### Flat
- `flatten(values: any[], depth: number = Infinity): T[]`##### Compare
- `isEqual(...arrays: T[][]): boolean`##### Map
- `map(values: S[], fn: (v: S) => T): T[]`
- `flatMap(values: any[], fn: ((v: S | S[]) => T | any) | ((v: S) => T), depth: number = Infinity): T[]`
- `convertMap(values: S[][], key: K): K[][]`##### Replace
- `replace(original: T, newValue: Partial): T`
- `replaceWithPredicate(original: T, newValue: Partial, predicate: (value: T) => boolean): T`
- `replaceAll(originalValues: T[], newValue: Partial): T[]`
- `replaceAllWithPredicate(originalValues: T[], newValue: Partial, predicate: (value: T) => boolean): T[]`##### Count
- `count(values: any[], depth: number = Infinity): number`
- `isEmpty(values: any[]): boolean`
- `unique(values: any[], depth = 0): T[]`##### Sort
- `sort(values: any[], sorter: Sorter, depth = 0): T[]`##### Remove
- `remove(values: any[], value: T): T[]`
- `flatRemove(values: any[], value: T): T[]`## Supported decorators
To enable the Pincet decorators, set `experimentalDecorators` in your tsconfig to true. This means that Typescript accepts custom decorators.##### Flatten
When you are only interested in a flat array, then you can use the Flatten decorator. It does exactly what is says:```
class Host {
@Flatten() values: T[];constructor(...values: any[]) {
this.values = values;
}
}const host = new Host(['aap', ['noot', ['mies']]]);
console.log(host.values); // ['aap', 'noot', 'mies']
```
Of course, with Pincet, you can almost always specify an optional depth:```
class Host {
@Flatten(2) values: T[];constructor(...values: any[]) {
this.values = values;
}
}const host = new Host(['aap', ['noot', ['mies']]]);
console.log(host.values); // ['aap', 'noot', ['mies']]
```##### Count
By default, the count decorator flattens the whole array. However, you can specify an optional depth.```
class Host {@Count() allValues: unknown[];
@Count(1) valuesWithDepthOne: unknown[];
constructor(...values: unknown[]) {
this.allValues = values;
this.valuesWithDepthOne = values;
}
}const host = new Host('aap', 'noot', ['mies', ['wim', ['zus']]]);
console.log(host.allValues); // 5
console.log(host.valuesWithDepthOne); // 4
```##### Empty
The empty decorator returns true when there are no values.```
class Host {@Empty() values: string[];
constructor(values: string[]) {
this.values = values;
}
}const host1 = new Host([]);
console.log(host1.values); // trueconst host2 = new Host(['aap']);
console.log(host2.values); // false
```##### Not empty
The not empty decorator returns true when there are values.```
class Host {@NotEmpty() values: string[];
constructor(values: string[]) {
this.values = values;
}
}const host1 = new Host(['aap']);
console.log(host1.values); // trueconst host2 = new Host([]);
console.log(host2.values); // false
```## Run tests
- Checkout locally
- Run `npm install`
- Run `npm run test`### Changelog
Just check CHANGELOG.md to see the latest technical changes.