1
\$\begingroup\$

I'm starting just now with CoffeeScript and I found out that I can't solve problems like looping and recursives with just one line. I would like to improve the code that I just wrote using built-in CoffeeScript helpers.

'use strict'
file_system = require 'fs'
Types       = require './types'

Extract =
  each_file: (index = 0) ->
    length  = @files.length
    file    = @files[index]
    if index < length
      Types.read file, @next.bind @
      @each_file index + 1

  next: (file) ->
    @result.push file
    if @result.length == @files.length
      return @cb.clean.call @cb, @result
    @each_file()
    return

  init: (files, cb) ->
    @files              = files
    @cb                     = cb
    @result             = []
    @each_file()
    return

module.exports = Extract

The script speaks for itself; I'm doing a recursive function to send files to Types.read, and I store the result in the result array.

\$\endgroup\$
2
  • \$\begingroup\$ And maybe link to used libraries. What is Types.read doing? \$\endgroup\$
    – ferada
    Commented Oct 11, 2015 at 13:45
  • \$\begingroup\$ It's other code in the app @ferada \$\endgroup\$ Commented Oct 11, 2015 at 15:58

1 Answer 1

8
+100
\$\begingroup\$

Fake for-loops and comprehensions

At the beginning of your post, you say that you can't always achieve everything with looping in one line in CoffeeScript. When you say that, I assume you are taking about the each_file:

  each_file: (index = 0) ->
    length  = @files.length
    file    = @files[index]
    if index < length
      Types.read file, @next.bind @
      @each_file index + 1

This is kinda ugly right now because you seem to be using a method to fake a for-loop. Luckily, with CoffeeScript's comprehensions, we can turn this into a simple 1-line expression.

First, we need to be iterating through all of the @files. That can be written simply like this:

Types.read(file, @next.bind this) for file in @files

This is a comprehension that will go through all of the @files and substitute the file in the Types.read call with the current file it's looping over.

Now that we have this, you can remove that

file = @files[index]

line, along with that single index parameter. Why? Because, now that we have this loop, we no longer have any of that method-recursion-fake-for-loop-idness.

Along with those, since this is now a comprehensions that loops through all the values in an array, we don't need to do any checking to make sure the index is less than the length.

if index < length

Can be removed. Guess what your method looks like now?

  each_file: () ->
    Types.read(file, @next.bind @) for file in @files

The above method will do exactly what it was doing before: it will go through all of the files in @files and pass it into Types.read along with @next.bind @.

What's different now?

  • It's much shorter and much simpler than what you were doing before. As I already stated, you seemed to be reinventing the for loop with that recursion you were doing.

  • It's more idiomatic. CoffeeScript has those comprehensions so you can simplify long tasks into a few small and readable lines.


Misc.

  init: (files, cb) ->
    @files              = files
    @cb                     = cb

This can be shortened to this:

  init: (@files, @cb) ->

The CoffeeScript compiler treats parameters with a @ before them as a name of a property to set to the parameter. For example, @files will become:

this.files = files

in the method body.

\$\endgroup\$
0

Not the answer you're looking for? Browse other questions tagged or ask your own question.