nvie / itertools Goto Github PK
View Code? Open in Web Editor NEWTypeScript port of Python's awesome itertools stdlib.
Home Page: https://www.npmjs.com/package/itertools
License: MIT License
TypeScript port of Python's awesome itertools stdlib.
Home Page: https://www.npmjs.com/package/itertools
License: MIT License
NOTE: There is an upcoming TC39 proposal, proposal-iterator-helpers, which will provide its own way to
run
afind
function like the one(s) below, but until then, the below function(s) seek to accomplish the same with full safety.
In line with the other iterable functions, having a built-in function that can retrieve the first matched entry or value in an iterable based on an accessor and an optional final parameter for the desired return value would be very helpful.
Some iterables have a values()
method to retrieve the values for that iterable. I think it could be useful to expose a separate method that accounts for that.
There is probably something already like this used elsewhere in this package to use instead, but including it for the sake of completeness in my definitions and examples
type TransformFunction<T, U> = (params: T) => U;
type KeyOrTransform<T, U> = keyof T | TransformFunction<T, U>;
type PossiblyValuesIterable<T> = { values(): IterableIterator<T> } | Iterable<T>;
export function createObjectSearcher<T, U>(searchFnOrKey?: null): null;
export function createObjectSearcher<T, U>(
searchFnOrKey: KeyOrTransform<T, U>
): TransformFunction<T, U>;
export function createObjectSearcher<T, U>(
searchFnOrKey?: null | KeyOrTransform<T, U>
) {
return typeof searchFnOrKey !== 'function' && searchFnOrKey != null
? (data: T) => data[searchFnOrKey as keyof T] as U
: searchFnOrKey;
}
find
definition/** Find first matching element from any iterable
* @param iterable - The iterable to search
* @param predicate - A function or key to extract the value to search for
* @param useValues - Whether to use `iterable.values()` instead of the iterable itself
* @returns The first matching element or undefined
*
* @example retrieving a value (from `iterable.values()`) where useValues=false
* find(values, v => value[1])[1]
* @example retrieving a value (from `iterable.values()`) where useValues=true
* find(values, v => value, true)
*/
export const find = <T, U extends (value: T) => unknown, K extends keyof T>(
iterable: PossiblyValuesIterable<T>,
predicate: U | K,
useValues: boolean = false
): T | undefined => {
const values =
useValues && 'values' in iterable
? iterable.values()
: (iterable as Iterable<T>);
const accessor = createObjectSearcher(predicate);
for (const item of values) {
if (accessor(item)) {
return item;
}
}
return undefined;
};
findValue
definitionfindValue
is essentially a light abstraction around find
for better readability
/** Same as find but presets the useValue param to true, as a decorator
* @see find
* @example BEFORE: using `find` to retrieve a value using `useValues=true` param
* find(values, v => value, true)
* @example AFTER: using `findValue` to retrieve a value
* findValue(values, v => value)
*
* ? About the same length to type `true` directly, but this is more readable
*/
export const findValue = <T, U extends (value: T) => unknown, K extends keyof T>(
iterable: PossiblyValuesIterable<T>,
predicate: U | K
): T | undefined => {
return find(iterable, predicate, true);
};
Note: when using find
with useValues=true
or using findValue
, this function will use the .values()
iterator for its iteration rather than the original iterator parameter value itself, if the iterator has a .values()
iterator.
To use the find
and findValue
function(s), pass in the iterable, an accessor function or a string representing a property name, and optionally a desired value function or a boolean. The function will iterate over the iterable and return the first matched value based on the accessor function or property.
Here are some examples:
const data = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
{ id: 3, name: "Charlie" },
];
// `find` and `findValue` would yield the same result in this example
const result = findValue(data, 'name');
console.log(result); // { id: 1, name: "Alice" }
const data = [
{ id: 1, score: 95 },
{ id: 2, score: 85 },
{ id: 3, score: 75 },
];
// `find` and `findValue` would yield the same result in this example
const result = findValue(data, (d) => d.score >= 90);
console.log(result); // { id: 1, score: 95 }
const data = [
{ id: 1, score: 95 },
{ id: 2, score: 85 },
{ id: 3, score: 75 },
];
// `find` and `findValue` would yield the same result in this example
const result = findValue(data, (d) => d.score >= 90)?.id;
console.log(result); // 1
const data = [
{ id: 1, score: 95 },
{ id: 2, score: 85 },
{ id: 3, score: 75 },
];
// `find` and `findValue` would yield the same result in this example
const result = findValue(data, (d) => d.score >= 90)?.id;
console.log(result); // 1
find
and findValue
when iterating over a Mapconst data = new Map([
[1, { name: "Alice", age: 30 }],
[2, { name: "Bob", age: 25 }],
[3, { name: "Charlie", age: 28 }],
]);
// `find` and `findValue` would NOT yield the same result in this example
// using `find` with a Map will iterate over the Map entries directly, with `useValues=false`
const result = find(data, (d) => d[1].age >= 30)?.[1].name;
// using `find` with `useValues=true` will iterate over the .values() iterator
const result = find(data, (d) => d.age >= 30, true)?.name;
// `findValue` is equivalent to—but arguably more readbale than—using `find` with `useValues=true`
const result = findValue(data, (d) => d.age >= 30)?.name;
console.log(result); // "Alice"
It seems the helpers in this package do not work when run on async-iterables.
E.g. the following will complain about missing iterable[Symbol.iterator]
?
Uncaught TypeError: iterable[Symbol.iterator] is not a function
Is the lack for support for async-iterables a known issue?
Maybe the documentation should state this?
Also, is this just a matter of extending the logic
to deal with the specifics of the async-iterables vs normal iterables?
Or is there a deeper constraint/issue here preventing support for those?
Consider these examples:
import { islice } from 'itertools';
function* positiveWholeNumbers() {
let n = 1;
while (true) {
yield n++;
}
}
const first5 = islice(positiveWholeNumbers(), 5);
console.log(Array.from(first5));
Expected behaviour: [1, 2, 3, 4, 5]
Current behaviour: Infinite loop
I really like your package! I have implemented the groupby
method on my side, I might actually do a PR at some point, but until then you can just reuse this if you want to (i.e. and type, test and document).
const groupby = function* groupby(iterable, key = x => x) {
const it = iter(iterable);
let currentValue;
let currentKey = {};
let targetKey = currentKey;
const grouper = function* grouper(tgtKey) {
while (currentKey === tgtKey) {
yield currentValue;
const { done, value } = it.next();
currentValue = value;
if (done) return;
currentKey = key(currentValue);
}
};
for (;;) {
while (currentKey === targetKey) {
const { done, value } = it.next();
currentValue = value;
if (done) return;
currentKey = key(currentValue);
}
targetKey = currentKey;
yield [currentKey, grouper(targetKey)];
}
};
console.log([...map(groupby('aaabbbbcddddAA'), ([k, v]) => [k, [...v].length])]);
/* [
[ "a", 3 ],
[ "b", 4 ],
[ "c", 1 ],
[ "d", 4 ],
[ "A", 2 ]
] */
The current package.json is assuming that since babel-runtime has a dependency on regenerator-runtime, it will be available for itertools to use. That is correct when using NPM or Yarn, but pnpm uses a different structure where the node_modules tree is not flat. In my case (webpack compiling a UI), I get:
spa/ui_main test: WEBPACK Compiling...
spa/ui_main test: WEBPACK Failed to compile with 4 error(s)
spa/ui_main test: Error in /home/jimp/dtr_code/momentum/node_modules/.registry.npmjs.org/itertools/1.3.2/node_modules/itertools/more-itertools.js
spa/ui_main test: Module not found: 'regenerator-runtime' in '/home/jimp/dtr_code/momentum/node_modules/.registry.npmjs.org/itertools/1.3.2/node_modules/itertools'
Hi :) Check your index.js in src folder, the permutation function is missing, when importing functions from './itertools'
Hi, sorry if this is a FAQ, but is there anything preventing the addtion of a tee function whithin this library (mimicking itertool's own tee : https://docs.python.org/3.9/library/itertools.html#itertools.tee) ?
import { tee } from itertools;
const iterable= {
*[Symbol.iterator]() {
yield "a";
yield "b";
yield "c";
yield "d";
yield "e";
},
};
const [iter1, iter2, iter3] = tee(iterable, 3)
console.log(...iter1);
console.log(...iter2);
console.log(...iter3);
// output:
// "a" "b" "c" "d" "e"
// "a" "b" "c" "d" "e"
// "a" "b" "c" "d" "e"
I'd bet there's a big intersection between users who like iterator-style (functional-style) programming and those who like static types, so please add support for Typescript!
I'd love to use this library, as I really like Python's itertools API, but instead I'll have to use iter-tools 😑
function * gen() {
for (let i = 0; ; i ++) {
yield i;
}
}
const generator = gen();
console.log([...islice(generator, 10)]);
Expected behavior: islice break execution after reach 10 elements
Current behavior: infinity loop
accumulate
, sometimes called scan
, is quite useful for turning certain loops into functional operations on iterators.
Please add it 🙏
(Remember the Typescript bindings 😁)
Looks like an oversight
I was looking for a "takeuntil" method across existing utils,
and the closest I found was takewhile
.
While takewhile
"produces elements from the iterable as long as the predicate is true",
I'd expect takeuntil
to "produce elements from the iterable as long as the predicate is true".
E.g.
test('takeWhile', () => {
expect([...takewhile([1, 2, -1, 3], x => x > 0)]).toStrictEqual([1, 2]);
});
test('takeuntil', () => {
expect([...takeuntil([1, 2, -1, 3], x => x < 0)]).toStrictEqual([1, 2, -1]);
});
Would it make sense to add this function to the library?
I.e. having
function* takeuntil(iterable, predicate) {
for (const value of iterable) {
yield value;
if (predicate(value)) return;
}
}
compared to
export function* takewhile<T>(iterable: Iterable<T>, predicate: Predicate<T>): Iterable<T> {
for (const value of iterable) {
if (!predicate(value)) return;
yield value;
}
}
Following code does not work for me
import groupby from 'itertools';
I got error:
TS7016: Could not find a declaration file for module 'itertools'. 'MY_PROJECT_PATH/node_modules/itertools/index.js' implicitly has an 'any' type. Try `npm install @types/itertools` if it exists or add a new declaration (.d.ts) file containing `declare module 'itertools';`
The current implementation of islice gets stuck in an infinite loop when the for...of tries to reach the end of the infinite iterator.
const pred = slicePredicate(start, stop, step); for (const [i, value] of enumerate(iterable)) { if (pred(i)) { yield value; } }
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.