import { Option } from "./Option"; import { WithEquality, hasTrueEquality, Ordering, ToOrderable } from "./Comparison"; import { HashMap } from "./HashMap"; import { Seq } from "./Seq"; import { Collection } from "./Collection"; import { Stream, ConsStream } from "./Stream"; import { Lazy } from "./Lazy"; import { HashSet } from "./HashSet"; /** * @hidden */ export function shuffle(array: any[]) { // https://stackoverflow.com/a/2450976/516188 var currentIndex = array.length, temporaryValue, randomIndex; // While there remain elements to shuffle... while (0 !== currentIndex) { // Pick a remaining element... randomIndex = Math.floor(Math.random() * currentIndex); currentIndex -= 1; // And swap it with the current element. temporaryValue = array[currentIndex]; array[currentIndex] = array[randomIndex]; array[randomIndex] = temporaryValue; } return array; } /** * @hidden */ export function arrangeBy(collection: Collection, getKey: (v:T)=>K&WithEquality): Option> { return Option.of(collection.groupBy(getKey).mapValues(v => v.single())) .filter(map => !map.anyMatch((k,v) => v.isNone())) .map(map => map.mapValues(v => v.getOrThrow())); } /** * @hidden */ export function seqHasTrueEquality(seq: Seq): boolean { return seq.find(x => x!=null).hasTrueEquality(); } /** * @hidden */ export function zipWithIndex(seq: Seq): Seq<[T,number]> { return seq.zip(Stream.iterate(0,i=>i+1)); } /** * @hidden */ export function sortOn(seq: Seq, getKeys: Array|{desc:ToOrderable}>): Seq { return seq.sortBy((x,y) => { for (const getKey of getKeys) { if ((getKey).desc) { const a = (>(getKey).desc)(x); const b = (>(getKey).desc)(y); if (a === b) { continue; } return a>getKey)(x); const b = (>getKey)(y); if (a === b) { continue; } return a>b?Ordering.GT:Ordering.LT; } } return Ordering.EQ; }); } /** * @hidden */ export function distinctBy(seq: Collection, keyExtractor: (x:T)=>U&WithEquality): Collection { let knownKeys = HashSet.empty(); return seq.filter(x => { const key = keyExtractor(x); const r = knownKeys.contains(key); if (!r) { knownKeys = knownKeys.add(key); } return !r; }); } /** * Utility function to help converting a value to string * util.inspect seems to depend on node. * @hidden */ export function toStringHelper( obj: any|null, options: {quoteStrings:boolean} = {quoteStrings: true}): string { if (Array.isArray(obj)) { return "[" + obj.map(o => toStringHelper(o, options)) + "]" } if (typeof obj === "string") { return options.quoteStrings ? `'${obj}'` : obj; } if (obj && (obj.toString !== Object.prototype.toString)) { return obj.toString(); } return JSON.stringify(obj); } /** * @hidden */ export function minBy(coll: Collection, compare: (v1:T,v2:T)=>Ordering): Option { return coll.reduce((v1,v2)=>compare(v1,v2)<0 ? v2 : v1); } /** * @hidden */ export function minOn(coll: Collection, getSortable: ToOrderable): Option { if (coll.isEmpty()) { return Option.none(); } let iter = coll[Symbol.iterator](); let step = iter.next(); let val = getSortable(step.value); let result = step.value; while (!(step = iter.next()).done) { const curVal = getSortable(step.value); if (curVal < val) { val = curVal; result = step.value; } } return Option.of(result); } /** * @hidden */ export function maxBy(coll: Collection, compare: (v1:T,v2:T)=>Ordering): Option { return coll.reduce((v1,v2)=>compare(v1,v2)>0 ? v2 : v1); } /** * @hidden */ export function maxOn(coll: Collection, getSortable: ToOrderable): Option { if (coll.isEmpty()) { return Option.none(); } let iter = coll[Symbol.iterator](); let step = iter.next(); let val = getSortable(step.value); let result = step.value; while (!(step = iter.next()).done) { const curVal = getSortable(step.value); if (curVal > val) { val = curVal; result = step.value; } } return Option.of(result); } /** * @hidden */ export function sumOn(coll: Collection, getNumber: (v:T)=>number): number { return coll.foldLeft(0, (soFar,cur)=>soFar+getNumber(cur)); } /** * @hidden */ export function reduce(coll: Collection, combine: (v1:T,v2:T)=>T): Option { if (coll.isEmpty()) { return Option.none(); } let iter = coll[Symbol.iterator](); let step = iter.next(); let result = step.value; while (!(step = iter.next()).done) { result = combine(result, step.value); } return Option.of(result); } /** * @hidden */ export function sliding(seq: Seq, count:number): Stream> { // in a way should get better performance with Seq.splitAt instead // of Seq.take+Seq.drop, but we should be lazy and not hold another // version of the sequence in memory (though for linked list it's free, // it's not the case for Vector) return seq.isEmpty() ? Stream.empty>() : new ConsStream(seq.take(count), Lazy.of(() => sliding(seq.drop(count), count))); }