adrianmc - 1 year ago 51

Javascript Question

For example, I want to get the dates of every Friday from now until 30 days from now.

Currently, I can make use of the underscore library and moment.js to do this. But the result is super verbose and annoyingly procedural/imperative. Observe:

`var initDate = moment().day("Friday");`

var endDate = moment().add(30, 'days');

var result = [];

result.push(initDate);

while (_.last(result).isBefore(endDate)) {

var x = _.last(result);

result.push(nextWeek(x));

}

alert(result); // answer here

// create a new moment from given moment and add 7 days

function nextWeek(initMoment) {

var x = moment(initMoment);

return x.add(7,'days');

}

Here is the corresponding fiddle: http://jsfiddle.net/aafsh5xa/

I was wondering if there's a way to use Haskell-like features such as list comprehensions or infinite lists (lazy evaluation) to make this a lot more concise. Maybe something like:

`var initDate = moment();`

var endDate = moment().add(30,days);

var everyFriday = genLazyList(initDate, nextFridayFrom);

var result = _.filter(everyFriday, function(input){ return input.isBefore(endDate); });

Note that everyFriday is an infinite list generated by genLazyList and is not evaluated until the _.filter() function is called upon it. And nextFridayFrom() is a function that genLazyList uses to make the lazy list.

Answer

Note I am using stream.js and moment.js in my answer. So you'll have to do the following includes in your HTML (note that my syntax is in jade):

```
script(src="path/to/stream.js")
script(src="path/to/moment.js")
```

And here is my code:

```
var initDate = moment().day("Friday");
var endDate = moment().add(30,'days');
function allFridays() {
return new Stream(initDate, function(){
return allFridays().map(function(date){return moment(date).add(7,'days')});
}
);
}
allFridays().takeWhile(function(h){return h.isBefore(endDate)}).print();
```

Or in my favourite flavour, coffeescript:

```
initDate = moment().day('Friday')
endDate = moment().add(30, 'days')
allFridays = ->
new Stream(initDate, ->
allFridays().map (date) ->
moment(date).add 7, 'days'
)
allFridays().takeWhile((h) -> h.isBefore endDate).print()
```

Isn't that just beautiful?

A quick explanation:

- I make an infinite stream of moment (date) objects which represent all fridays from now until forever
- I take (i.e. retrieve) the elements of this stream up until the first moment which does not represent a time before my endDate.

And that's it!

Note that the `takeWhile()`

function is not yet implemented in the official stream.js release, but I simply copy and pasted it in from its lib folder. See its git repo here.