Nodejs Google Drive API - Accessing array elements in callback function - node.js

I want file names produced using drive.files.list to use as strings for downloading all files in a folder.
I am having trouble accessing an array of filenames. Basically through lack of knowledge, So I am lost by the logic of the mouse over reference of map in files.map() See below code.
My code:
if (files.length) {
// map method calls the callbackfn one time for each element in the array
files.map((file) => {
// there are x elements (a filename/id) called x times
// I want to access one filename at a time. Return a plain string filename for a downloadFile() function
var names = [];
// rough test to produce desired output. Produces 'undefined' for array indexes greater than 0 e.g names[1]
// files.length = 1;
// Without the files.length = 1; filename is outputted x times
var names = [];
files.forEach(function (file, i) {
names[i]= file.name;
})
// first array index (with files.length =1;) first filename and only this filename. Correct result!!!
console.log(names[0]);
I don't have much experience with OOP or Nodejs. But using various tests (code changes) the largest output looked like an array of arrays. I want to narrow it down to an array of filenames that I can JSON.stringify before using for downloading.
Mouse over of 'map'
(method) Array<drive_v3.Schema$File>.map<void>(callbackfn: (value: drive_v3.Schema$File, index: number, array: drive_v3.Schema$File[]) => void, thisArg?: any): void[]
Calls a defined callback function on each element of an array, and returns an array that contains the results.
#param callbackfn — A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.
#param thisArg — An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
Any suggestions apprecieated.

Try replacing the code with this one:
if (files.length) {
var names = [];
files.forEach(function(file, i) {
names.push(file.name);
});
console.log('filenames: ', names);
}
and then you can read the filenames by looping through names array, and pass it to other functions for processing:
names.forEach((fileName, i)=>{
console.log('filename: ', fileName, ' index', i);
// pass the name to other functions for processing
download(fileName);
});

Related

How can I get a call back to modify a string?

write a function named speaker that takes in an array of strings and a callback function.
Use forEach to build a new array of strings, each string modified by the callback. Return the new array.
const speaker = (words, callback) => {
};
Honestly have zero clue where to start
Most likely you want to write a function in JavaScript. It can be implemented like this:
const speaker = (words, callback) => {
let modifiedWords = [];
words.forEach((word) => {
// Add the modified string to the array
modifiedWords.push(callback(word));
});
return modifiedWords;
};
To begin with, we define an array modifiedWords, where after each iteration we will add the modified word with the callback function. Next, we use the forEach method for the words array. This method iterates over each element of the array and calls the callback function that we passed (we passed an anonymous arrow function that takes an array element as an argument and passes this element as an argument to our callback function and returns the result). The result is stored in the array modifiedWords with the push method. We then return an array of modified words modifiedWords.

Is it absolutely the case that arrays are used by reference in other modules in Node.js?

I have
// file cars.js
var bodyshop = require('./bodyshop')
var connections = [];
many functions which operate on connections. adding them, changing them etc.
code in this file includes things like
bodyshop.meld(blah)
bodyshop.mix(blah)
exports.connections = connections
and then
// file bodyshop.js
let cars = require('./cars');
even more functions which operate on connections. adding them, changing them etc.
code in this file includes things like
cars.connections[3].color = pink
cars.connections.splice(deleteMe, 1)
module.exports = { meld, mix, flatten }
Is it absolutely honestly the case that code in bodyshop such as cars.connections.splice(deleteMe, 1) will indeed delete an item from "the" connections (ie, the one and only connections, declared in cars.js) and code in bodyshop such as cars.connections[3].color = pink will indeed change the color of index 3 of "the" self-same one and only connections?
Is it quite OK / safe / acceptable that I used the syntax "module.exports = { }" at the end of bodyshop, rather than three lines like "exports.meld = meld" ?
Is this sentence indeed to totally correct?? "In Node.js if you export from M an array, when using the array in another module X which requires M, the array will be by reference in X, i.e. not by copy" ... ?
I created two files with the following methods and the array as you mentioned.
First File: test1.js
const testArray = [];
const getArray = () => {
return testArray;
};
module.exports = {
testArray,
getArray
}
Second File: test2.js
const { testArray, getArray } = require('./test1');
console.log('testing the required array before modifying it');
console.log(getArray());
testArray.push('test');
console.log('testing the method result after modifying the required array content');
console.log(getArray());
If you can create the mentioned files and run them locally, you will see the following result.
>node test2.js
testing the required array before modifying it
[]
testing the method result after modifying the required array content
[ 'test' ]
The points observed is,
yes, it's okay if you want to export it with the syntax module.exports = { }, It not much an issue.
If any of the methods modify this array outside of the required file, it will affect here as well, This because require will be a reference, not a copy.
The one possible solution will be creating a JSON copy of it while requiring as below:
const { testArray, getArray } = require('./test1');
const testArrayCopy = JSON.parse(JSON.stringify(testArray));
console.log('testing the required array before modifying it');
console.log(getArray());
testArrayCopy.push('test');
console.log('testing the method result after modifying the required array content');
console.log(getArray());
This is the result:
>node test2.js
testing the required array before modifying it
[]
testing the method result after modifying the required array content
[]
Note: JSON copy will not help you in parsing DateTime properly.

Cannot convert object to primitive value when attempting to run word count on array created from Firebase records

I am trying to write a node js program that reads values from a Firebase database and aggregates all the words in a specific field for all the
records, but I am getting the below errors ..
[2019-06-24T14:52:14.083Z] #firebase/database: FIREBASE WARNING: Exception was thrown by user callback. TypeError: Cannot convert object to primitive value at C:\Users\xxx\Projects\NodeProjects\QuestionAppNode\index.js:52:38
TypeError: Cannot convert object to primitive value
Below is my node.js code ..
retrieveQuestions();
function retrieveQuestions(){
userQuestionsFBRef.once("value", function(snapshot) {
var fetchedQuestions = [];
snapshot.forEach(function(snapshotChild){
var itemVal = snapshotChild.val();
fetchedQuestions.push(itemVal);
})
var arrayOfQuestions = [];
fetchedQuestions.forEach(function(question){
arrayOfQuestions += question.question
})
console.log("Fetched questions are " + JSON.stringify(fetchedQuestions));
console.log("arrayOfQuestions is " +JSON.stringify(arrayOfQuestions));
var wordcnt = arrayOfQuestions.replace(/[^\w\s]/g, "").split(/\s+/).reduce(function(map, word){
map[word] = (map[word]||0)+1;
return map;
}, Object.create(null));
console.log("Word count is " + wordcnt)
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
}
The similar code does work if I run it in Chrome console i.e.
var arrayOfQuestions = [{"dateTime":"2019-06-24T14:06:36.983Z","name":"AMA09","question":"Who let the dogs out?"},{"dateTime":"2019-06-24T14:07:11.501Z","name":"AMA09","question":"What is the capital of Senegal?"},{"dateTime":"2019-06-24T14:20:25.222Z","name":"AMA34","question":"Is Free will an illusion?"}];
var newArray = [];
arrayOfQuestions.forEach(question => newArray += question.question);
var wordcnt = newArray.replace(/[^\w\s]/g, "").split(/\s+/).reduce(function(map, word){
map[word] = (map[word]||0)+1;
return map;
}, Object.create(null));
Does anyone have any idea why this is happening?
I realise that the approach I am taking to aggregate the words in these records is probably not the correct way to go i.e. adding all the text in the
question field of the fb records is probably a bit stupid and wouldn't work for large datasets so if someone could offer any suggestions on a
different approach that would be appreciated as well.
Many thanks.
The problem appears to be this line:
console.log("Word count is " + wordcnt)
Since wordcnt is an object without a prototype, that is, Object.create(null), it has no toString method, hence the error "TypeError: Cannot convert object to primitive value".
Solution 1 - Use object literal syntax in your reduce expression:
var wordcnt = arrayOfQuestions
.replace(/[^\w\s]/g, "")
.split(/\s+/)
.reduce(function(map, word){
map[word] = (map[word]||0)+1;
return map;
}, {}); // Object literal instead of Object.create(null)
This creates an object with the usual Object prototype, which has a toString method.
Solution 2 - Don't concatenate in console.log but rather use multiple arguments:
console.log("Word count is", wordcnt) // instead of " + wordcnt)
This allows console.log to do its normal stringifying of objects.
Solution 3 - Convert the wordcnt map to a json string.
console.log("Word count is " + JSON.stringify(wordcnt))
This converts your object to a JSON representation of itself.

Concurrently iterate over two iterables of same length

I have two iterables of the same length that I need to loop over at the same time. One iterable is a Map of custom objects, and the other is an array of objects. I need to add the contents of the array into the Map (via some helper prototype functions), preferably asynchronously and concurrently. Also, the two containers are associated to each other based on their order. So the first element in the array needs to be added to the first element in the Map.
If I was to do this synchronously it would look something like this:
var map;
var arr;
for (var i = 0; i < arr.length; i++) {
// get our custom object, call its prototype helper function with the values
// in the array.
let customObj = map[i];
customObj.setValues(arr[i])
}
Typically to loop over arrays async and concurrently I use bluebirds Promise.map. It would look something like this:
var arr
Promise.map(arr, (elem) => {
// do whatever I need to do with that element of the array
callAFunction(elem)
})
It would be awesome if I could do something like this:
var map;
var arr;
Promise.map(map, arr, (mapElem, arrElem) {
let customObj = mapElem[1];
customObj.setValue(arrElem);
})
Does anyone know of a library or a clever way to help me accomplish this?
Thanks.
EDIT: Just want to add some clarification on the objects stored in the map. The map is keyed on a unique value, and values are associated with that unique values are what make up this object. It is defined in a similar manner to this:
module.exports = CustomObject;
function CustomObject(options) {
// Initialize CustomObjects variables...
}
CustomObject.prototype.setValue(obj) {
// Logic for adding values to object...
}
if you already know, that the Map (I assume you really mean the JavaScript Map here, which is ordered) and the array have the same length, you do not need a mapping function, that takes both the array AND the map. One of both is enough, because the map function also gives you an index value:
var map;
var arr;
Promise.map(map, (mapElem, index) => {
let customObj = mapElem[1];
customObj.setValue(arr[index]);
});
You can use the function Promise.all that execute all the given asynchronous functions.
You should know that actually node.js support fully Promises, you do not need bluebirds anymore.
Promise.all(arr.map(x => anyAsynchronousFunc(x)))
.then((rets) => {
// Have here all return of the asynchronous functions you did called
// You can construct your array using the result in rets
})
.catch((err) => {
// Handle the error
});

How do I get the result of class getters into JSON? [duplicate]

Take this object:
x = {
"key1": "xxx",
"key2": function(){return this.key1}
}
If I do this:
y = JSON.parse( JSON.stringify(x) );
Then y will return { "key1": "xxx" }. Is there anything one could do to transfer functions via stringify? Creating an object with attached functions is possible with the "ye goode olde eval()", but whats with packing it?
json-stringify-function is a similar post to this one.
A snippet discovered via that post may be useful to anyone stumbling across this answer. It works by making use of the replacer parameter in JSON.stringify and the reviver parameter in JSON.parse.
More specifically, when a value happens to be of type function, .toString() is called on it via the replacer. When it comes time to parse, eval() is performed via the reviver when a function is present in string form.
var JSONfn;
if (!JSONfn) {
JSONfn = {};
}
(function () {
JSONfn.stringify = function(obj) {
return JSON.stringify(obj,function(key, value){
return (typeof value === 'function' ) ? value.toString() : value;
});
}
JSONfn.parse = function(str) {
return JSON.parse(str,function(key, value){
if(typeof value != 'string') return value;
return ( value.substring(0,8) == 'function') ? eval('('+value+')') : value;
});
}
}());
Code Snippet taken from Vadim Kiryukhin's JSONfn.js or see documentation at Home Page
I've had a similar requirement lately. To be clear, the output looks like JSON but in fact is just javascript.
JSON.stringify works well in most cases, but "fails" with functions.
I got it working with a few tricks:
make use of replacer (2nd parameter of JSON.stringify())
use func.toString() to get the JS code for a function
remember which functions have been stringified and replace them directly in the result
And here's how it looks like:
// our source data
const source = {
"aaa": 123,
"bbb": function (c) {
// do something
return c + 1;
}
};
// keep a list of serialized functions
const functions = [];
// json replacer - returns a placeholder for functions
const jsonReplacer = function (key, val) {
if (typeof val === 'function') {
functions.push(val.toString());
return "{func_" + (functions.length - 1) + "}";
}
return val;
};
// regex replacer - replaces placeholders with functions
const funcReplacer = function (match, id) {
return functions[id];
};
const result = JSON
.stringify(source, jsonReplacer) // generate json with placeholders
.replace(/"\{func_(\d+)\}"/g, funcReplacer); // replace placeholders with functions
// show the result
document.body.innerText = result;
body { white-space: pre-wrap; font-family: monospace; }
Important: Be careful about the placeholder format - make sure it's not too generic. If you change it, also change the regex as applicable.
Technically this is not JSON, I can also hardly imagine why would you want to do this, but try the following hack:
x.key2 = x.key2.toString();
JSON.stringify(x) //"{"key1":"xxx","key2":"function (){return this.key1}"}"
Of course the first line can be automated by iterating recursively over the object. Reverse operation is harder - function is only a string, eval will work, but you have to guess whether a given key contains a stringified function code or not.
You can't pack functions since the data they close over is not visible to any serializer.
Even Mozilla's uneval cannot pack closures properly.
Your best bet, is to use a reviver and a replacer.
https://yuilibrary.com/yui/docs/json/json-freeze-thaw.html
The reviver function passed to JSON.parse is applied to all key:value pairs in the raw parsed object from the deepest keys to the highest level. In our case, this means that the name and discovered properties will be passed through the reviver, and then the object containing those keys will be passed through.
This is what I did https://gist.github.com/Lepozepo/3275d686bc56e4fb5d11d27ef330a8ed
function stringifyWithFunctions(object) {
return JSON.stringify(object, (key, val) => {
if (typeof val === 'function') {
return `(${val})`; // make it a string, surround it by parenthesis to ensure we can revive it as an anonymous function
}
return val;
});
};
function parseWithFunctions(obj) {
return JSON.parse(obj, (k, v) => {
if (typeof v === 'string' && v.indexOf('function') >= 0) {
return eval(v);
}
return v;
});
};
The naughty but effective way would be to simply:
Function.prototype.toJSON = function() { return this.toString(); }
Though your real problem (aside from modifying the prototype of Function) would be deserialization without the use of eval.
I have come up with this solution which will take care of conversion of functions (no eval). All you have to do is put this code before you use JSON methods. Usage is exactly the same but right now it takes only one param value to convert to a JSON string, so if you pass remaning replacer and space params, they will be ignored.
void function () {
window.JSON = Object.create(JSON)
JSON.stringify = function (obj) {
return JSON.__proto__.stringify(obj, function (key, value) {
if (typeof value === 'function') {
return value.toString()
}
return value
})
}
JSON.parse = function (obj) {
return JSON.__proto__.parse(obj, function (key, value) {
if (typeof value === 'string' && value.slice(0, 8) == 'function') {
return Function('return ' + value)()
}
return value
})
}
}()
// YOUR CODE GOES BELOW HERE
x = {
"key1": "xxx",
"key2": function(){return this.key1}
}
const y = JSON.parse(JSON.stringify(x))
console.log(y.key2())
It is entirely possible to create functions from string without eval()
var obj = {a:function(a,b){
return a+b;
}};
var serialized = JSON.stringify(obj, function(k,v){
//special treatment for function types
if(typeof v === "function")
return v.toString();//we save the function as string
return v;
});
/*output:
"{"a":"function (a,b){\n return a+b;\n }"}"
*/
now some magic to turn string into function with this function
var compileFunction = function(str){
//find parameters
var pstart = str.indexOf('('), pend = str.indexOf(')');
var params = str.substring(pstart+1, pend);
params = params.trim();
//find function body
var bstart = str.indexOf('{'), bend = str.lastIndexOf('}');
var str = str.substring(bstart+1, bend);
return Function(params, str);
}
now use JSON.parse with reviver
var revivedObj = JSON.parse(serialized, function(k,v){
// there is probably a better way to determ if a value is a function string
if(typeof v === "string" && v.indexOf("function") !== -1)
return compileFunction(v);
return v;
});
//output:
revivedObj.a
function anonymous(a,b
/**/) {
return a+b;
}
revivedObj.a(1,2)
3
To my knowledge, there are no serialization libraries that persist functions - in any language. Serialization is what one does to preserve data. Compilation is what one does to preserve functions.
It seems that people landing here are dealing with structures that would be valid JSON if not for the fact that they contain functions. So how do we handle stringifying these structures?
I ran into the problem while writing a script to modify RequireJS configurations. This is how I did it. First, there's a bit of code earlier that makes sure that the placeholder used internally (">>>F<<<") does not show up as a value in the RequireJS configuration. Very unlikely to happen but better safe than sorry. The input configuration is read as a JavaScript Object, which may contain arrays, atomic values, other Objects and functions. It would be straightforwardly stringifiable as JSON if functions were not present. This configuration is the config object in the code that follows:
// Holds functions we encounter.
var functions = [];
var placeholder = ">>>F<<<";
// This handler just records a function object in `functions` and returns the
// placeholder as the value to insert into the JSON structure.
function handler(key, value) {
if (value instanceof Function) {
functions.push(value);
return placeholder;
}
return value;
}
// We stringify, using our custom handler.
var pre = JSON.stringify(config, handler, 4);
// Then we replace the placeholders in order they were encountered, with
// the functions we've recorded.
var post = pre.replace(new RegExp('"' + placeholder + '"', 'g'),
functions.shift.bind(functions));
The post variable contains the final value. This code relies on the fact that the order in which handler is called is the same as the order of the various pieces of data in the final JSON. I've checked the ECMAScript 5th edition, which defines the stringification algorithm and cannot find a case where there would be an ordering problem. If this algorithm were to change in a future edition the fix would be to use unique placholders for function and use these to refer back to the functions which would be stored in an associative array mapping unique placeholders to functions.

Resources