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
    Oct 11, 2015 at 13:45
  • \$\begingroup\$ It's other code in the app @ferada \$\endgroup\$ 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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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