learnyounode Lesson 5 – Filtered LS

In this lesson we need to created a program that prints a listed of files in a directory, filtered by extension.  As with the previous lessons, getting a filtered list of files in a directory is a built in utility in the command line interface.

ls *.txt

In this case, the file extension supplied by learnyounode will not include the ‘.’ prefix, which means we will need to account for this in our code.  Additionally, we must get our filtered list using asynchronous input/output.

In the previous lessons we learned how to use the file system module in our code.  We will need to do this again.  However, the file system module does not include a built in method for getting file extensions (that I am aware of). We could do some fancy string manipulation to get the path from the filenames using the file system module by passing the complete filename to a couple of JavaScript’s built in methods.

filename = 'myFile.txt'
extension = filename.split('.').pop();
console.log(extension)

This will work, but lucky for us we don’t need to waste our time trying to figure out a clever method to derive extensions from filenames because there is already a node module that does this for us.  This module is known as the path module.

var path = require('path')
console.log(path.extname('myFile.txt'))

That was easy, and we don’t need to worry about packaging this into a function so that it can be reused.  The hard work has already been done.

Official Solution

var fs = require('fs')
var path = require('path')

var folder = process.argv[2]
var ext = '.' + process.argv[3]

fs.readdir(folder, function (err, files) {
  if (err) return console.error(err)
  files.forEach(function(file) {
      if (path.extname(file) === ext) {
          console.log(file)
      }
  })
})

The official solution are passes the file system module to the variable fs, and the path module to path.  It also passes the third and fourth index of the argv property of the process object.  After this, the program calls the readdir() method from the fs module, which accepts folder and a callback function as arguments.  The callback function accepts an error and files as arguments.

When the callback function runs, we check for an error and return and print an error message if an error occurs.  At the same time, we call the forEach() method on files.  The files.forEach() method accepts a callback function as an argument.  The callback function accepts a file as an argument.  The file is passed to this function from the files.forEach() method, which receives files from the fs.readdir() method.  If the path of file equals the variable ext (parocess.argv[3]), file is logged to the console.

My Solution

As you work on this challenge, you might be wondering how you can make your solution more reusable.  The function in the code below can be reused for different purposes.  The next lesson shows us how to take this code and make it modular so that it can be passed to a variable like the filesystem or path module.

var fs = require('fs')
var path = require('path')

var dir = process.argv[2]
var filterStr = process.argv[3]

function getFiles(dir, filterStr, callback) {

  fs.readdir(dir, function (err, list) {
    if (err)
      return callback(err)

    list = list.filter(function (file) {
      return path.extname(file) === '.' + filterStr
    })

    callback(null, list)
  })
}

getFiles(dir, filterStr, function (err, list) {
  if (err)
    return console.error('There was an error:', err)

  list.forEach(function (file) {
    console.log(file)
  })
})

In the solution above, the file system module is passed to the variable fs, and the path module to path.  Then the third and fourth index of the argv property of the process object are assigned to variables, just like the official solution.  Instead of calling the fs.readdir() method directly, A function called getFiles() with the fs.readdir() method inside of it is declared.

The getFiles() function accepts a directory (dir), filter string (filterStr), and a callback function as arguments.  Inside of getFiles(), the fs.readdir() method is called with the variable ‘dir’ and a callback as arguments.  The callback accepts error (err), and list as arguments.  One way to think of this is that the fs.readdir) method will call the callback function when it is done and give it a list.

Inside of the callback the filter() method is used to pass the value of list.filter() the list variable.  The filter() method accepts a callback function as an argument.  The callback accepts file as an argument.  Inside of the callback function, The file is returned to the list variable by checking to see if the file extension is equal to filterStr with the path.extname() method.

Additionally, inside of the callback that fs.readdir() is passed to, the callback is called and passed ‘null’ and the ‘list’ variable.  This is what passes the contents of the list when getFiles is called.

After getFiles() function is declared, it is called it and passed the arguments ‘dir’, ‘filterStr’, and a callback.  The callback function accepts ‘err’ and ‘list’ as arguments.  Inside of the callback errors are checked for and returned if the statement evaluates to true.  The forEach() method is used to loop through the list and passed a callback function to the forEach() method.  Inside of the callback a file from our list variable is passed as an argument and then the contents of the file variable, which has our filename, is printed to the console with the console.log() method.

In the next lesson, Make it Modular, you will see how this solution can be further modularized by passing it to the exports property of the module object.

Lesson 6

Contents