UNPKG

locutus

Version:

Locutus other languages' standard libraries to JavaScript for fun and educational purposes

281 lines (280 loc) 9.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.toIterableArray = toIterableArray; exports.parseZipLongestArgs = parseZipLongestArgs; exports.parseProductArgs = parseProductArgs; exports.itertoolsAccumulate = itertoolsAccumulate; exports.itertoolsBatched = itertoolsBatched; exports.itertoolsChain = itertoolsChain; exports.itertoolsCompress = itertoolsCompress; exports.itertoolsIslice = itertoolsIslice; exports.itertoolsPairwise = itertoolsPairwise; exports.itertoolsZipLongest = itertoolsZipLongest; exports.itertoolsCombinations = itertoolsCombinations; exports.itertoolsCombinationsWithReplacement = itertoolsCombinationsWithReplacement; exports.itertoolsPermutations = itertoolsPermutations; exports.itertoolsProduct = itertoolsProduct; const _operator_ts_1 = require("./_operator.js"); function toIterableArray(iterable, functionName) { if (Array.isArray(iterable)) { return [...iterable]; } if (typeof iterable === 'string') { return [...iterable]; } if (isIterable(iterable)) { return Array.from(iterable); } throw new TypeError(`${functionName}() expected an iterable`); } function parseZipLongestArgs(args) { if (args.length === 0) { return { iterables: [], fillvalue: null }; } const options = extractIterableOptions(args[args.length - 1]); if (!options) { return { iterables: args, fillvalue: null }; } return { iterables: args.slice(0, -1), fillvalue: options.fillvalue ?? null, }; } function parseProductArgs(args) { if (args.length === 0) { return { iterables: [], repeat: 1 }; } const options = extractRepeatOptions(args[args.length - 1]); if (!options) { return { iterables: args, repeat: 1 }; } return { iterables: args.slice(0, -1), repeat: toNonNegativeInteger(options.repeat ?? 1, 'product'), }; } function itertoolsAccumulate(iterable, func) { const values = toIterableArray(iterable, 'accumulate'); if (values.length === 0) { return []; } const callback = func ?? defaultAccumulateStep; const result = [values[0]]; for (let index = 1; index < values.length; index += 1) { result.push(callback(result[result.length - 1], values[index])); } return result; } function itertoolsBatched(iterable, n) { const values = toIterableArray(iterable, 'batched'); const batchSize = toPositiveInteger(n, 'batched', 'n must be at least one'); const result = []; for (let index = 0; index < values.length; index += batchSize) { result.push(values.slice(index, index + batchSize)); } return result; } function itertoolsChain(iterables) { return iterables.flatMap((iterable) => toIterableArray(iterable, 'chain')); } function itertoolsCompress(data, selectors) { const values = toIterableArray(data, 'compress'); const masks = toIterableArray(selectors, 'compress'); const result = []; for (let index = 0; index < values.length && index < masks.length; index += 1) { if ((0, _operator_ts_1.pythonTruth)(masks[index])) { result.push(values[index]); } } return result; } function itertoolsIslice(iterable, args) { const values = toIterableArray(iterable, 'islice'); const { start, stop, step } = parseSliceArgs(args); const result = []; for (let index = start; index < stop && index < values.length; index += step) { result.push(values[index]); } return result; } function itertoolsPairwise(iterable) { const values = toIterableArray(iterable, 'pairwise'); const result = []; for (let index = 0; index + 1 < values.length; index += 1) { result.push([values[index], values[index + 1]]); } return result; } function itertoolsZipLongest(args) { const { iterables, fillvalue } = parseZipLongestArgs(args); const pools = iterables.map((iterable) => toIterableArray(iterable, 'zip_longest')); const width = pools.reduce((max, pool) => Math.max(max, pool.length), 0); const result = []; for (let row = 0; row < width; row += 1) { result.push(pools.map((pool) => (row < pool.length ? pool[row] : fillvalue))); } return result; } function itertoolsCombinations(iterable, r) { const pool = toIterableArray(iterable, 'combinations'); const size = toNonNegativeInteger(r, 'combinations'); const result = []; buildCombinations(pool, size, 0, [], result); return result; } function itertoolsCombinationsWithReplacement(iterable, r) { const pool = toIterableArray(iterable, 'combinations_with_replacement'); const size = toNonNegativeInteger(r, 'combinations_with_replacement'); const result = []; if (size === 0) { return [[]]; } if (pool.length === 0) { return []; } buildCombinationsWithReplacement(pool, size, 0, [], result); return result; } function itertoolsPermutations(iterable, r) { const pool = toIterableArray(iterable, 'permutations'); const size = r === undefined ? pool.length : toNonNegativeInteger(r, 'permutations'); const result = []; if (size > pool.length) { return []; } buildPermutations(pool, size, [], new Set(), result); return result; } function itertoolsProduct(args) { const { iterables, repeat } = parseProductArgs(args); const basePools = iterables.map((iterable) => toIterableArray(iterable, 'product')); const pools = repeat === 1 ? basePools : Array.from({ length: repeat }, () => basePools).flatMap((poolSet) => poolSet.map((pool) => [...pool])); if (pools.length === 0) { return [[]]; } if (pools.some((pool) => pool.length === 0)) { return []; } const result = []; buildProduct(pools, 0, [], result); return result; } function defaultAccumulateStep(left, right) { return (0, _operator_ts_1.pythonAdd)(left, right, 'accumulate'); } function parseSliceArgs(args) { if (args.length === 0) { throw new TypeError("islice() missing required argument 'stop'"); } if (args.length > 3) { throw new TypeError(`islice expected at most 4 arguments, got ${args.length + 1}`); } let start = 0; let stop = 0; let step = 1; if (args.length === 1) { stop = toNonNegativeInteger(args[0], 'islice'); } else { start = toNonNegativeInteger(args[0], 'islice'); stop = toNonNegativeInteger(args[1], 'islice'); if (args.length === 3) { step = toPositiveInteger(args[2], 'islice', 'step for islice() must be a positive integer or None'); } } return { start, stop, step }; } function buildCombinations(pool, size, start, current, result) { if (current.length === size) { result.push([...current]); return; } for (let index = start; index <= pool.length - (size - current.length); index += 1) { current.push(pool[index]); buildCombinations(pool, size, index + 1, current, result); current.pop(); } } function buildCombinationsWithReplacement(pool, size, start, current, result) { if (current.length === size) { result.push([...current]); return; } for (let index = start; index < pool.length; index += 1) { current.push(pool[index]); buildCombinationsWithReplacement(pool, size, index, current, result); current.pop(); } } function buildPermutations(pool, size, current, usedIndexes, result) { if (current.length === size) { result.push([...current]); return; } for (let index = 0; index < pool.length; index += 1) { if (usedIndexes.has(index)) { continue; } usedIndexes.add(index); current.push(pool[index]); buildPermutations(pool, size, current, usedIndexes, result); current.pop(); usedIndexes.delete(index); } } function buildProduct(pools, index, current, result) { if (index >= pools.length) { result.push([...current]); return; } const pool = pools[index] ?? []; for (const value of pool) { current.push(value); buildProduct(pools, index + 1, current, result); current.pop(); } } function toPositiveInteger(value, functionName, message) { const integer = toNonNegativeInteger(value, functionName); if (integer < 1) { throw new Error(message ?? `${functionName}() expected a positive integer`); } return integer; } function toNonNegativeInteger(value, functionName) { if (typeof value !== 'number' || !Number.isFinite(value) || !Number.isInteger(value) || value < 0) { throw new TypeError(`${functionName}() expected a non-negative integer`); } return value; } function isIterable(value) { return ((typeof value === 'object' || typeof value === 'function') && value !== null && Symbol.iterator in value && typeof value[Symbol.iterator] === 'function'); } function extractIterableOptions(value) { if (!isPlainObject(value)) { return null; } const keys = Object.keys(value); if (keys.length === 0 || keys.some((key) => key !== 'fillvalue')) { return null; } return value; } function extractRepeatOptions(value) { if (!isPlainObject(value)) { return null; } const keys = Object.keys(value); if (keys.length === 0 || keys.some((key) => key !== 'repeat')) { return null; } return value; } function isPlainObject(value) { return typeof value === 'object' && value !== null && Object.getPrototypeOf(value) === Object.prototype; }