How to filter “locally and remotely” in functional programming

  softwareengineering

My example applies to reading and deleting files (I/O), but this is probably a common scenario (eg, keeping local and global state in sync in functional programming).

I am reading in files from a directory.

import { readdirSync } from 'fs'

const files = readdirSync('./myFolder')

I want to filter out the operating system file .DS_STORE from the local list, but not only do I want to filter it out of the list, I also want to delete it from the file system.

My first approach here looks like this:

import { readdirSync, unlinkSync } from 'fs'

const files = readdirSync('./myFolder')

const filteredList = files.filter(file => {
  if (file.endsWith('.DS_STORE')) {
    unlinkSync(file) // <-- side effect
    return false
  }

  return true
})

My problem here is that the filter is performing a side effect to keep local and global state in sync.

I could isolate the side effect like this:

import { readdirSync, unlinkSync } from 'fs'

const files = readdirSync('./myFolder')

const filteredList = files
  .map((file) => {
    if (file.endsWith('.DS_STORE')) unlinkSync(file)
    return file
  })
  .filter((file) => !file.endsWith('.DS_STORE'))

I’ve now isolated the side effect, but I now have to do the endsWith check twice, and I potentially would move the string .DS_STORE to a variable that is closured for both the map and the filter to use. I also feel weird about using a map to just perform a side effect and then pass along the data, although maybe I shouldn’t worry too much about that.

Is there another approach here that is more elegant? Am I maybe overthinking this? From an FP practitioner’s point of view, is the first filter-only example still an acceptable solution?

LEAVE A COMMENT