Reputation: 372
I have solved the sum-of-multiples small puzzle like this:
function sum(multiples, limit_in) {
return [...multiples.map(n => [...range(n, limit_in, n)])
.reduce((numbers, array) => {
array.forEach(value => numbers.add(value));
return numbers;
}, new Set())
.values()]
.reduce((sum, n) => sum + n, 0);
}
function* range(start, stop, step = 1) {
if (stop == null) {
stop = start;
start = 0;
}
for (let i = start; step > 0 ? i < stop : i > stop; i += step) {
yield i;
}
}
const result = sum([5, 6, 8], 150);
console.log(result); // The result is 4419
Second version was something like this:
function sum(multiples, limit_in) {
return [...multiples.flatMap(n => [...range(n, limit_in, n)])
.reduce((numbers, value) => {
numbers.add(value);
return numbers;
}, new Set())
.values()]
.reduce((sum, n) => sum + n, 0);
}
function* range(start, stop, step = 1) {
if (stop == null) {
stop = start;
start = 0;
}
for (let i = start; step > 0 ? i < stop : i > stop; i += step) {
yield i;
}
}
const result = sum([5, 6, 8], 150);
console.log(result); // Result 4419
The question is it possible to make it more lazy?
Is there a way to flatten more the generator (function*): range(n, limit_in, n)
and not construct the array [...range(n, limit_in, n)]
?
Upvotes: 2
Views: 205
Reputation: 11347
Javascript standard library is not good at dealing with iterators. If you want a completely lazy, memory-savvy solution, you'll have to define your own set of generic iteration primitives, for example:
function* range(start, stop, step = 1) {
if (stop == null) {
stop = start;
start = 0;
}
for (let i = start; step > 0 ? i < stop : i > stop; i += step) {
yield i;
}
}
function* map(it, fn) {
for (let x of it)
yield fn(x)
}
function* chain(iters) {
for (let it of iters)
yield* it
}
function* uniq(it) {
let s = new Set
for (let x of it) {
if (!s.has(x)) {
s.add(x)
yield x
}
}
}
function sum(it) {
let s = 0
for (let x of it)
s += x
return s
}
let solution = (multiples, limit_in) =>
sum(
uniq(
chain(
map(
multiples,
n => range(n, limit_in, n)))))
const result = solution([5, 6, 8], 150);
console.log(result); // The result is 4419
Upvotes: 2
Reputation: 351118
You could write another helper function that allows to reduce the values originating from an iterable:
function sum(multiples, limit_in) {
return iterReduce(
multiples.reduce((numbers, n) =>
iterReduce(
range(n, limit_in, n),
(numbers, n) => numbers.add(n),
numbers
),
new Set
),
(sum, n) => sum + n,
0
);
}
function iterReduce(iterable, callback, acc) {
const it = iterable[Symbol.iterator]();
if (arguments.length < 3) acc = it.next();
for (const val of it) {
acc = callback(acc, val);
}
return acc;
}
function* range(start, stop, step = 1) {
if (stop == null) {
stop = start;
start = 0;
}
for (let i = start; step > 0 ? i < stop : i > stop; i += step) {
yield i;
}
}
const result = sum([5, 6, 8], 150);
console.log(result); // The result is 4419
Upvotes: 1