Skip to content

Instantly share code, notes, and snippets.

@mrozbarry
Created May 20, 2021 21:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrozbarry/df9eef5d8749808dc34bb3dcfdf72159 to your computer and use it in GitHub Desktop.
Save mrozbarry/df9eef5d8749808dc34bb3dcfdf72159 to your computer and use it in GitHub Desktop.
A friend of mine was really _taken_ by Haskell's `take <count> 1..5` functionality, and while there were naysayers, I said it could be duplicated in javascript with generators. Here it is, in all of it's glory
function *makeNumbers(numberStep = 1, startAt = 0) {
for(number = startAt; ; number += numberStep) yield number;
}
export class Range {
constructor(start = 0, end = 0, step = 1) {
this._from = start;
this._to = end;
this._step = step;
}
from(number) {
return new Range(number, this._to, this._step);
}
to(number) {
return new Range(this._from, number, this._step);
}
step(number) {
return new Range(this._from, this._to, number);
}
toArray() {
const iterator = makeNumbers(this._step, this._from);
const maxIterations = Math.abs(this._to - this._from) + 1;
const collect = (iteration = 0) => {
if (iteration >= maxIterations) return [];
const result = iterator.next();
if (result.done) return [];
return [
result.value,
...collect(iteration + 1),
];
}
return collect();
}
slice(start, end) {
const step = end > start ? Math.abs(this._step) : Math.abs(this._step) * -1;
const _end = end >= 0 ? end : start + end;
return this.from(start).to(_end).step(step).toArray();
}
take(count) {
const iterator = makeNumbers(this._step, this._from);
return Array.from(
{ length: count },
() => {
const result = iterator.next();
if (result.done) return null;
return result.value;
},
).filter(v => v !== null)
}
}
export const range = (words, min, max) => {
const [def] = words.filter(w => w);
return (new Range())
.from(min)
.to(def === '...' ? max : max - 1)
.toArray();
};
export const take = (count, pattern) => {
const [first, second] = pattern;
const diff = Math.abs(second - first);
const direction = Math.sign(second - first);
return (new Range())
.step(diff * direction)
.from(first)
.take(count);
}
import { Range, range, take } from './range.js';
// The haskell clone
console.log(
take(5, [3, 7])
),
// Output: [3, 7, 11, 15, 19]
// And other interesting things
// Compact
console.log(
(new Range(0, 10, 2)).toArray()
);
// Output: [0, 2, 4, 6, 8, 10]
// Verbose
console.log(
(new Range())
.from(0)
.to(10)
.step(2)
.toArray()
);
// Output: [0, 2, 4, 6, 8, 10]
// Fancy-pants, but no steps
console.log(
range`${0}...${10}`
);
// Output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment