Looping on FolderIterator and FileIterator - Google Apps Script - javascript

I'm making a script to loop in a Google Drive folder and noticed I don't know what's the best way to loop over iterators (specifically FolderIterator and FileIterator).
My first approach
const drive_id = '__________';
let base_folder = DriveApp.getFolderById(drive_id),
sub_folders = base_folder.getFolders();
Logger.log(base_folder.getFolders());
for(let sub_folder of sub_folders)
{
Logger.log(sub_folder);
}
outputs Error TypeError: sub_folders is not iterable.
Secondly I tried with while loops and checks on hasNext(), but I didn't find them beautifyl enough. I'm currently set with this for loop, that works and is clean enough but still looks a bit hacky.
/*...*/
for(let sub_folders = base_folder.getFolders(); sub_folders.hasNext();)
{
let sub_folder = sub_folders.next();
Logger.log(sub_folder);
}
Also notice how I couldn't declare sub_folder within the loop itself. So, what's the proper way of doing it?
Also, what if I wanted to use a map or filter on the sub_folders instead?
Note: this question is probably of broader scope than google-script, but I couldn't find the right terminology to ask the question differently so I preferred to stick to the particular case I found.

What's the proper way of looping a FolderIterator or FileIterator?
The proper way of doing it by using a while loop.
The reason for that has to do with the hasNext() function which returns a Boolean (true or false) and the only loop that natively works with booleans is the while loop.
Also, what if I wanted to use a map or filter on the sub_folders instead?
In your code sub_folder is an object of type Folder. Unfortunately, you can only retrieve the folders one by one within the while loop by using next() so you can't directly apply map or filter on the FolderIterator.
Instead, you can create an array of folders by pushing each folder to an array (within the while loop) and then use map.
Example:
This script finds all the folders and store them in an array and then uses map to get the name of each sub folder:
function myFunction() {
const base_folder = DriveApp.getFolderById('folder_id');
const sub_folders = base_folder.getFolders();
const folders = [];
while (sub_folders.hasNext()){
let folder = sub_folders.next()
folders.push(folder);
}
const folder_names = folders.map(f=>f.getName());
console.log(folder_names);
}
Here is a list of all the available methods you can apply to each element of the folders array: Class Folder

Related

`for` loops vs `.map` to iterate arrays

Is there a specific reason why is better to use .map than for loops in React?
I'm working on a project where all arrays are being iterated with for loops but I'm convinced that is better and good practice to use .map because it creates a copy of the array, that to my understanding is better practice but I can't find a specific reason.
Is there a specific reason why is better to use .map than for loops in React?
If you're just iterating, map is the wrong tool. map is for mapping arrays: producing a new array based on the values from the previous array. Someone somewhere is teaching map as an iteration tool, unfortunately, doing their students a disservice. (I wish I knew who it was so I could have a word.) Never do this:
// Don't do this
myArray.map(entry => {
// ...do something with `entry`...
});
For iteration, it's a choice between a for loop, a for-of loop, and the forEach method. (Well, and a few other things; see my answer here for a thorough rundown.)
For instance, using forEach
myArray.forEach(entry => {
// ...do something with `entry`...
});
vs. using for-of:
for (const entry of myArray) {
// ...do something with `entry`...
}
(Those aren't quite equivalent. The former has to be an array. The latter can be any iterable object.)
The reason you may see map a lot in React is that you're frequently mapping things in React, in at least two ways:
Mapping from raw data to JSX elements, like this:
return (
<div>
{myData.map(({id, name}) => <div key={id}>{name}</div>)}
</div>
);
Since that's a mapping operation, with the array created by map being used to provide the contents of the outer div, map is the right choice there.
Mapping from old state to new state, like this:
const [myArray, setMyArray] = useState([]);
// ...
setMyArray(myArray.map(obj => {...obj, update: "updated value"}));
Since that again is a mapping operation, creating a new array to set as the myArray state member, map is the right choice.
...but I'm convinced that is better and good practice to use .map because it creates a copy of the array...
If you want a copy/updated version of the array, yes, map is a good choice. It's more concise than the equivalent for loop (or even for-of):
const newArray = oldArray.map(entry => /*...update...*/);
vs.
// Probably not best practice unless you have conditional logic
// in the loop body that may or may not `push` (or similar)
const newArray = [];
for (const entry of oldArray) {
newArray.push(/*...update...*/);
}
.map() maps each array value to a new value, and returns a brand new array.
In React.js context, .map() can be used to map each array item to a piece of JSX fragment.
for loop also iterates over an array, just like .map(). The major difference is that you can specify custom computation with for loop. Whereas .map() is specifically designed for mapping

Arr.push inside For-loop

I have a SPA built with Vue and I'm trying to fetch an array of items from a custom API.
Once I've stored the items I'm trying to loop through that array and push in a custom path value, however I get the error
this.list[i].push is not a function
I'm not sure why this wouldn't be possible, but I'm sure I've missed something.
this.list: []
fetch(){
let url = 'http://core.com/api/v0/sources/'
axios.get(url).then((response) => {
this.list = response.data.data
for(var i = 0; i < this.list.length; i++){
let arr = { path: '/testPath' }
this.list[i].push(arr)
}
})
}
In the comments you mentioned your goal:
"this.list[i].path does not exist, that is what Im trying to create"
...if you literally want to add a property called "path" directly under the object at this.list[i] then you'd write
this.list[i].path = '/testPath'
This will create a new property for the object being held at this.list[i].
P.S.
You don't need your (badly-named) arr variable at all here.
Using .push() doesn't make any sense (and doesn't work) because this.list[i] contains an object rather than an array, and also doing that wouldn't create a property as you wanted.
push is the function of an array but you are trying to push an object in an object that's why you got this exception.
you can do as below.
this.list[i].path = '/testPath'
path property will be dynamically added in your object this.list[i].

Add output to an array of each for loop

I'm trying to call a REST API using for loop and store the output to an array. But the output shows in different arrays for each and every rest API call instead of everything in one array
for each (name in car.cars){
for(i=0; i<count;i++){
var arr = [];
var newid = car.cars[i].name;
var url = "myhost"
var method = "GET"
var response = "output from REST call"
arr.push(response)
}
}
But the output shows in different arrays for each and every rest API call instead of everything in one array
Where is "the output" there's nothing in your code.
Your Problem is that you're declaring the var arr = []; inside your for loop.
Initialize the array before the loop starts and just add your responses to that array.
Instead your creating a new array for each iteration of for(i=0; i<count;i++)
Take a step back and look at your code again. Where are you declaring your array? Inside the loop.
Next, ask yourself; what is the scope of that array? Only within the loop. Even if just in the outer loop, it won't stick around because as soon as that loop finishes, the array disappears.
Create the array and use it from outside the loops (both of them).
Further reading: Declaring variables inside or outside of a loop
UPDATE 4/30/2019: Thanks to #AuxTaco, I crossed out my inaccurate description of scope in JS. Please see the links in his comment for more reading on this!

How to iterate through a linked list in javascript

Someone shared this beautifully elegant way to create a linked list from an array.
function removeKFromList(l, k) {
let list = l.reduceRight((value, next)=>({next, value}), null);
console.log(list);
}
let l = [3, 1, 2, 3, 4, 5];
let k = 3;
removeKFromList(l, k);
It's pretty easy to iterate arrays (for, filter, map, reduce, etc.) but a linked list has none of those features. I need to iterate through to remove k values from the list. I'm guessing I need to create a variable for the current node and use a while loop, but there's no documentation on this. I've seen some repl code doing it but it seems unnecessarily complicated.
How do you iterate through a linked list in javascript?
First of all, although the idea of using reduce is indeed beautiful, I must say that the result is not so good, because the resulting nodes have a field "next" where the value is and a field "value" where the next is, i.e. they are swapped. So let's fix that:
function removeKFromList(l, k) {
let list = l.reduceRight((value, next)=>({value: next, next: value}), null);
console.log(list);
}
Secondly, the name of that function is awful, it should be named "arrayToLinkedList" or something more suggestive. Also, logging the result does not make sense, we should return it instead. Moreover, the parameter k is simply unused. Fixing those things:
function arrayToLinkedList(array) {
return array.reduceRight((prev, cur) => ({ value: cur, next: prev }), null);
}
Now, let's work on how to iterate over this. What do you think? I will give some hints because giving the answer directly probably won't help you learn much:
Observe that the linked list itself is:
Either the value null, or
A plain object with two fields, one field "value" which can be anything, and one field "next" which is a linked list.
Observe that the above (recursive) definition is well-defined and covers all cases.
Now, use that to your assistence. How do you get the first value? That would be simply myList.value, right? Now how do I get the second value? By applying .next, we get the next linked list, and so on. If next is null, you know you have to stop.
Let me know if you need further assistance.
EDIT: I noticed that you're looking for the right way to make an iterating method on lists with an idea of "adding it to the prototype" or something. So, about that:
To add an instance method by a prototype, you'd need your linked lists to be a class. That would be overcomplicating, unless you really have a good reason to define a class for this (which would be creating several methods and utility things around it).
It is much better, in my opinion, to just define a function that takes one linked list as the first parameter and a callback function as the second parameter, similarly to lodash's .each:
function forEachValueInLinkedList(linkedList, callback) {
// here you loop through all values and call
// callback(value)
// for each value.
}
I'd say this would be the most "javascriptonic" way to do it.

Push variable outside nested functions

I'm not sure why this isn't working so if anyone can help that would be great. I have nested functions and I wand to push the value returned by the firebase query and re use it in another firebase query, and then again, in a loop. Its essentially a poor man's infinite scroll. Nonetheless, I cannot get the value of the "arr" variable into the "numWanted" array outside so I can use it again in the next loop. What can I do to achieve the desired result?
Also, I have have beed trying to make the inner variables global, and push them out to another variable but that doesn't seem to work. Possible that I am just doing it wrong?
Thanks in advance..
$scope.loadMoreData = function() {
var numWanted = [];
console.log(numWanted);
firebase
.database()
.ref('products')
.orderByChild('rank')
.startAt(0)
.endAt(numWanted)
.limitToLast(3)
.once('value', function(products) {
products.forEach(function(product) {
var product = {
rank: product.val().rank
};
arr = product.rank;
});
numWanted.push(arr);
console.log(numWanted);
});
};
P.S. I realize this code doesn't actually work as you cannot use an array in a firebase query. My plan is to extract the number I need once the array has been populated.
You have a conflict with your parameter named product and a local variable named product. You need to rename one of those.

Categories