brandonscript
brandonscript

Reputation: 73015

More concise way to convert an array of files into object, using the filename as key

I'm using Node.js to load an array of files from a folder and create an array of objects:

var files = {}
fs.readdirSync(dir).forEach(function(file) {
    files[file] = fs.readFileSync(dir + '/' + file)
})

This seems like I should be able to do this without defining the object first, using .map() maybe?

Upvotes: 2

Views: 227

Answers (3)

Travis J
Travis J

Reputation: 82307

Using map won't reproduce your example. It will construct an array of objects. This entire exercise is essentially a micro optimization and what you have works and is readable so I would suggest just using it.

That said, you could move the object definition if you really wanted to, and I will show a small example here of that just to show options

var files = fs.readdirSync(dir).reduce(function(obj,file) {
    return (obj[file] = fs.readFileSync(dir + '/' + file),obj)
},{})

Upvotes: 1

Mike Cluck
Mike Cluck

Reputation: 32511

As others have mentioned, you could do it with this (arguably uglier) reduce.

var files = fs.readdirSync(dir).reduce(function(files, file) {
  files[file] = fs.readFileSync(dir + '/' + file);
  return files;
}, {});

To make this nicer to use, you could wrap it in a helper function or even extend the Array prototype (not suggested but it does look cleaner).

function log(msg) {
  document.querySelector('pre').innerText += msg + '\n';
}

var filesInDir = ['a.txt', 'b.txt', 'c.txt'];
var files;

// Helper function
function toObject(arr, map) {
  return arr.reduce(function(obj, value) {
    obj[value] = map(value);
    return obj;
  }, {});
};

files = toObject(filesInDir, function(file) {
  return file + '_mapped';
});
log('Helper');
log(JSON.stringify(files, null, 2));


// Modify the prototype (not recommended)
Array.prototype.toObject = function(map) {
  return this.reduce(function(obj, value) {
    obj[value] = map(value);
    return obj;
  }, {});
};

files = filesInDir.toObject(function(file) {
  return file + '_mapped';
});

log('Prototype');
log(JSON.stringify(files, null, 2));
<pre></pre>

Upvotes: 0

user663031
user663031

Reputation:

In theory you could do

Object.assign({}, ...fs.readdirSync(dir) .
  map(
   file => ({[file]: fs.readFileSync(dir + '/' + file})
  )
);

Whether or not that is more "concise" is up to you.

The reduce solution would be

fs.readdirSync(dir) .
  reduce(
    (result, file) => {
      result[file] = fs.readFileSync(dir + '/' + file);
      return result;
    },
    {}
  );

or

fs.readdirSync(dir) . 
  reduce(
    (result, file) =>
      result.defineProperty(file, {
        value: fs.readFileSync(dir + '/' + file)
      }),
    {}
  );

None of these seems as obvious as the code you propose.

Underscore can transform an array of pairs into an object, so you could do

_.object(fs.readdirSync(dir) . 
  map(
    file =>
      [file, fs.readFileSync(dir + '/' + file]
  )
);

Upvotes: 0

Related Questions