How can I get a call back to modify a string? - 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.

Related

Nodejs Google Drive API - Accessing array elements in callback function

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);
});

nodejs map return nothing when if statement true

so i have the following code
data.push(commands.map(
command => {
if (!command.devOnly) { return command.name; } // first condition
if (command.devOnly && message.author.id != '3251268789058714880') {} // second condition
},
).join('\n'));
if the second condition is true it returns null but then when I run console.log(data) it has a blank line for all the commands where the second condition is true.
Is there a way to stop the second condition from returning anything, and not leaving the blank line
.map() is a 1-for-1 transformation so the output array will have EXACTLY the same number of elements in it as the input array. If you don't return anything, that element in the array will have an undefined value (which is the return value when you don't actively return something).
To transform the array and eliminate some elements, you cannot use .map(). You can either do .filter().map() where you first filter out the items you don't want and then map the others or you can use a regular for loop and just push the items into output array that you want to keep using either a regular for loop iteration or a .reduce() or .forEach() iteration.
One example:
const results = commands.filter(command => !command.devOnly).map(command => command.name);
console.log(results);
const results = [];
for (let command of commands) {
if (!command.devOnly) results.push(command.name);
}
console.log(results);
Note, your second condition doesn't do anything at all in your code example so I wasn't sure how to account for that in these examples.
P.S. I've often wished Javascript had a .filterMap() feature that let you return undefined to leave that value out of the result - otherwise work like .map(). But, it doesn't have that feature built in. You could build your own.
Per your comments, you can filter on two conditions like this:
const results = commands
.filter(command => !command.devOnly || message.author.id === '3251268789058714880')
.map(command => command.name);
console.log(results);
you could use Array.reduce
commands.reduce((prev,command)=>{
if (!command.devOnly)
return [...prev,command.name]
// this if, is useless but shows that you can use more conditions.
if (command.devOnly && message.author.id != '3251268789058714880')
return prev
// add more conditions as you need here
return prev
},[])
another options could be by doing map then filter the undefined values, or use for-loop
like jfriend00 explained in his answer. the filterMap() he wished could be implemented with Array.reduce

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
});

Protractor compare string numbers

Today I've faced interesting problem of create test for pretty simple behavior: 'Most recent' sorting. All what test need to know:
Every item have ID
Previous ID is less then next in this case of sorting
Approach: writing ID in to attribute of item, getting that id from first item with getAttribute() and either way for second.
Problem: getAttribute() promise resulting with string value and Jasmine is not able to compare (from the box) string numbers.
I would like to find elegant way to compare them with toBeLessThan() instead of using chains of few .then() that will be finished with comparing that things.
Root of no-type-definition evil
Thanks guys <3
You can create a helper function to convert string number to actual number, which will make use of Promises:
function toNumber(promiseOrValue) {
// if it is not a promise, then convert a value
if (!protractor.promise.isPromise(promiseOrValue)) {
return parseInt(promiseOrValue, 10);
}
// if promise - convert result to number
return promiseOrValue.then(function (stringNumber) {
return parseInt(stringNumber, 10);
});
}
And then use the result with .toBeLessThan, etc:
expect(toNumber(itemId)).toBeLessThan(toNumber(anotherItemId));
I forgot of native nature of promises but tnx to Michael Radionov I've remembered what I want to do.
expect(first.then( r => Number(r) )).toBe(next.then( r => Number(r) ));
I guess this stroke looks simple.
UPDATE
ES6:
it('should test numbers', async function () {
let first = Number(await $('#first').getText());
let second = Number(await $('#second').getText());
expect(first).toBeGreaterThan(second);
})
One option to approach it with a custom jasmine matcher:
toBeSorted: function() {
return {
compare: function(actual) {
var expected = actual.slice().sort(function (a, b) {
return +a.localeCompare(+b);
});
return {
pass: jasmine.matchersUtil.equals(actual, expected)
};
}
};
},
Here the matcher takes an actual input array, integer-sort it and compare with the input array.
Usage:
expect(element.all(by.css(".myclass")).getAttribute("id")).toBeSorted();
Note that here we are calling getAttribute("id") on an ElementArrayFinder which would resolve into an array of id attribute values. expect() itself is patched to implicitly resolve promises.

What can be done on elements returned by elementsByXYZ?

This question is about functional tests written with the Intern.
The elementsByXYZ methods returns an array of elements. I've noticed that I'm able to call the method click() on these returned elements, but I cannot for example call the method getAttribute(attributeName).
What is the list of methods that can be called on elements returned by a a elementsByXYZ method ?
Here is a code snippet that illustrates what I'm trying to achieve:
return this.remote
.get(require.toUrl("./testpage.html"))
.waitForCondition('ready', 5000)
.elementById('widget1')
.elementsByTagName('div')
.then(function(children){
assert.equal(7, children.length, 'The expected number of children is wrong');
for(var i=0; i < 7; i++){
console.log(children[i].getAttribute('className'));
children[i].click();
}
});
The console shows that children[i].getAttribute('className') returns undefined, while I can see that the clicks are correctly performed on each child.
Wouldn't you need to use something like:
elem.getAttribute('class', function (err, text) {
console.log(text);
)};
When using wd.js? e.g. https://github.com/admc/wd/blob/master/doc/jsonwire-mapping.md
getAttribute(element, attrName, cb) -> cb(err, value)
That works for me.

Resources