collection.map is not a function at Promise.all.then

Viewed 2087

Alright, guys

I'm new to promises and am trying to map the result of loading in CSVs via promises, to then perform subsequent applications on it. Is Async mapping allowed? Is there an alternative approach to map after using promises? Or am I being stupid and missing something completely simple?

var csv = (url) => new Promise(function(resolve) {
    d3.csv(url, function(data) {
        data.forEach(function(d) { 
            d.String = d.String;
            d.Integer = +d.Integer;
        })
    resolve(data);
    });
});

Promise.all([ 
    csv('content.csv'),
    csv('moreContent.csv')
])
.then(([csvOne, csvTwo]) => { 
    var collection = { csvOne, csvTwo };

    var aveageMap = {};

    collection.map((d,i) => {
        if (!aveageMap[d.String]) {
            aveageMap[d.String] = {
                sum: 0,
                average: 0
            };
        }
        var theWord = aveMap[d.String];
        theWord.sum += +d.Integer;
        theWord.average = theWord.sum / collection.length;
    });

    console.log(aveageMap);

});

For the comments and answers below

Using an array is an object, but returns a complete merger of the two CSVs... Which affects the theWord.sum / collection.length line... Whilst it works to some degree, I wanted to use an array of objects so that I could get an output like the one below, and access each object independently if needed.

collection {
    dataCSV {
        String: , Integer: ,
        String: , Integer: ,
    },
    dataCSV2 {
        String: , Integer: ,
        String: , Integer: ,
    }
}
3 Answers

A few things:

  • It looks like you are trying to combine the two arrays returned by csv, which you can do via csvOne.concat(csvTwo), or [...csvOne, ...csvTwo] (using es6).
  • Since you're not actually returning everything in your mapping function, it would be more appropriate to use .forEach.
  • To me, it also looks like d.word should be d.String (unless they are the same). Also note that many consider it bad style to have capitalized property names (especially those with already existing class names, like String), so you may want to rename d.String and d.Integer to d.string and d.integer or something more descriptive. It also likes like ageMapshould be aveageMap.

All together, it looks like:

var csv = (url) => new Promise(function(resolve) {
    d3.csv(url, function(data) {
        data.forEach(function(d) { 
            d.string = d['String'];
            d.integer = +d['Integer'];
        })
        resolve(data);
    });
});
Promise.all([ 
    csv('content.csv'),
    csv('moreContent.csv')
])
.then(([csvOne, csvTwo]) => { 
    var aveageMap = {};
    var collection = [...csvOne, ...csvTwo];
    collection.forEach((d,i) => {
        if (!aveageMap[d.string]) {
            aveageMap[d.string] = {
                sum: 0,
                average: 0
            };
        }
        var theWord = aveageMap[d.string];
        theWord.sum += +d.integer;
        theWord.average = theWord.sum / collection.length;
    });    
    console.log(aveageMap);

});

You can't call map on a non-array-object, if you want to be able to call map make it an array instead of an object.

var collection = [ csvOne, csvTwo ];

Based on how i understand your question.

Promise.all([csv('content.csv'),csv('moreContent.csv')]).then(([...csvs])=>{
  const collection = {};
  csvs.forEach(csv=>{
    if(!collection[csv.String]) 
      collection[csv.String] = {sum:0, average:0}
      .....
  })
});
Related