locutus
Version:
Locutus other languages' standard libraries to JavaScript for fun and educational purposes
75 lines (74 loc) • 2.59 kB
JavaScript
function stablePartitionKey(value) {
if (value === null) {
return 'null';
}
if (typeof value === 'undefined') {
return 'undefined';
}
if (typeof value === 'number') {
if (Number.isNaN(value)) {
return 'number:NaN';
}
if (Object.is(value, -0)) {
return 'number:-0';
}
return `number:${value}`;
}
if (typeof value === 'string') {
return `string:${value}`;
}
if (typeof value === 'boolean') {
return `boolean:${value}`;
}
if (typeof value === 'bigint') {
return `bigint:${value}`;
}
if (Array.isArray(value)) {
return `array:[${value.map((item) => stablePartitionKey(item)).join(',')}]`;
}
if (typeof value === 'object') {
const record = value;
const entries = Object.keys(record)
.sort()
.map((key) => `${JSON.stringify(key)}:${stablePartitionKey(record[key])}`);
return `object:{${entries.join(',')}}`;
}
return `${typeof value}:${String(value)}`;
}
export function partition_by(selector, values) {
// discuss at: https://locutus.io/clojure/partition_by/
// parity verified: Clojure 1.12
// original by: Kevin van Zonneveld (https://kvz.io)
// note 1: Groups consecutive values while selector(value) remains equal, similar to Clojure partition-by.
// example 1: partition_by((value) => value % 2 === 0, [1, 1, 2, 2, 3, 4, 5, 5])
// returns 1: [[1, 1], [2, 2], [3], [4], [5, 5]]
// example 2: partition_by((value) => value.length, ['a', 'ab', 'cd', 'efg'])
// returns 2: [['a'], ['ab', 'cd'], ['efg']]
// example 3: partition_by((value) => value, [])
// returns 3: []
if (typeof selector !== 'function') {
throw new TypeError('partition_by(): selector must be a function');
}
if (!Array.isArray(values) || values.length === 0) {
return [];
}
const out = [];
let currentBucket = [];
let previousKey = null;
for (let i = 0; i < values.length; i++) {
const value = values[i];
const nextKey = stablePartitionKey(selector(value, i, values));
if (previousKey === null || nextKey === previousKey) {
currentBucket.push(value);
previousKey = nextKey;
continue;
}
out.push(currentBucket);
currentBucket = [value];
previousKey = nextKey;
}
if (currentBucket.length > 0) {
out.push(currentBucket);
}
return out;
}