How to refactor this function to a higher order function?
It is meant to return a new array containing the sub-arrays of characters that contain the value 'Rambo'.
function isRamboCharacter(characters) {
const x = [];
for (let i = 0; i < characters.length; i++) {
if (characters[i].movie.includes('Rambo')) {
x.push(characters[i]);
}
}
return x;
}
I tried:
return characters.movie.includes('Rambo');
Solution
const characters = [
{ movie: ["Rambo", "Rockey", "Avengers"] },
{ movie: ["Fatherhood", "Rockey", "Avengers"] }
]
const isRamboCharacter = (characters) => characters.filter((char) => char.movie.includes("Rambo"))
console.log(isRamboCharacter(characters));
Or you could directly call Array's filter method
console.log(characters.filter((char) => char.movie.includes("Rambo")));
//output - [ { movie: [ 'Rambo', 'Rockey', 'Avengers' ] } ]
You can curry it (Return a function in a function)
function isCharacterInMovie(movie) {
return function filterFunction(characters) {
const x = [];
for (let i = 0; i < characters.length; i++) {
if (characters[i].movie.includes(movie)) {
x.push(characters[i]);
}
}
return x;
}
}
const isRamboInMovie = isCharacterInMovie('rambo') // returns the inner function which holds on to the value from when you called it
isRamboInMovie(charactersInTheMatrix); // []
isRamboInMovie(charactersInRambo); // ['rambo']
The 'You don't know JS' book series has a great (short AND free) book on this, and it explains it incredibly well
You Don't Know JS Yet: Scope & Closures - 2nd Edition
Related
I'm creating a polyfill of Array.flat() method, however, I'm facing issues while calling the function within itself after checking that the looped element is an array and thats need to be flattened further. When a write a code that is not prototypal, the flattening is proper, however when I try to create a prototype function, I'm unable to get the flattened array. I'm pretty sure that the issue is related with the 'this' keyword. Please have a look at my code.
Here is the code
let arrayFlat = [1, 2, 3, [4, 5, 6, [7, 8, [9]], 10, [11, 12]], [13, [14, 15]]];
const flatArray = (array) => {
let output = [];
const flatten = (array) => {
for (let i = 0; i < array.length; i++) {
if (Array.isArray(array[i])) {
flatten(array[i]);
} else {
output.push(array[i]);
}
}
return output;
};
return flatten(array);
};
Array.prototype.myFlat = function () {
let output = [];
for (let i = 0; i < this.length; i++) {
if (Array.isArray(this[i])) {
console.log(this[i]);
this[i].myFlat();
} else {
output.push(this[i]);
}
}
return output;
};
In your first piece of code, you create a single output array. When you recursively call flatten, the code is always pushing to the exact same output array, which is in the closure of flatten. Then once everything is done, you return that array.
In the second code, you create a new array every time you recurse. Each recursion will create an array, flatten itself, and then return that new array. But the return value is ignored, so these values don't go anywhere.
You have a few options
Make the code basically identical to your first one, with an internal function for doing the recursion, and a closure variable used by all:
Array.prototype.myFlat = function () {
let output = [];
const flatten = (array) => {
for (let i = 0; i < array.length; i++) {
if (Array.isArray(array[i])) {
flatten(array[i]);
} else {
output.push(array[i]);
}
}
return output;
};
return flatten(this);
}
Pass the output array as a parameter when you recurse:
// VVVVVV--- added parameter
Array.prototype.myFlat = function (output = []) {
for (let i = 0; i < this.length; i++) {
if (Array.isArray(this[i])) {
this[i].myFlat(output); // <---- forward the array along
} else {
output.push(this[i]);
}
}
return output;
};
Continue having separate arrays, but then merge them together as the stack unwinds:
Array.prototype.myFlat = function () {
let output = [];
for (let i = 0; i < this.length; i++) {
if (Array.isArray(this[i])) {
output.push(...this[i].myFlat()); // <---- added output.push
} else {
output.push(this[i]);
}
}
return output;
};
I am a strong proponent of keeping classes as thin as possible, wrapping functional interfaces wherever possible -
function myFlat(t) {
return Array.isArray(t)
? t.reduce((r, v) => r.concat(myFlat(v)), [])
: [t]
}
Array.prototype.myFlat = function() { return myFlat(this) }
console.log([1,[2,[3],4],[[5]],6,[[[7]]]].myFlat())
// [ 1, 2, 3, 4, 5, 6, 7 ]
I've only been studying javascript for three weeks now and need help on a task.
I want to implement a function, called test, which takes a list and a function as arguments. The test function should call the function that was submitted as an argument once for each value in the list, with the list value in question as an argument, and will return a NEW list containing only the list values for which the argument function returned true. And I want to do that WITHOUT using the filter() method.
How can I think here? Is my code below a good start? I appreciate all the help I can get here so I can understand this.
let x = ["1", "2", "3"];
function test(x, s) {
for (let i = 0; i < x.length; i++) {
}
return
}
The code you provided is a good start, it provides a way of looping through all the elements in the list, which is a good starting point. I do suggest however that you change the names of your arguments so that they better represent the data they hold.
Your next step is to call the function passed in as an argument (f) for each element in your list and check whether or not it returns true. If it does, then you can add this element to a new list (which holds the list of values to return).
Once your loop is complete, you can return this new list. Take a look at an example below to get an understand of how you might implement this:
let x = [1, 2, 3, 4];
function test(lst, f) {
let new_list = [];
for (let i = 0; i < lst.length; i++) {
if(f(lst[i])) { // check if the function gives back a "truthy" value
new_list.push(lst[i]);
}
}
return new_list;
}
let result = test(x, function(list_element) {
return list_element % 2 === 0; // return true if element is even
});
console.log(result);
You could also achieve this in a number of different ways, such as using .reduce() and a ternary to make the method nice and concise:
const x = [1, 2, 3, 4];
const test = (lst, f) =>
lst.reduce((acc, elem) => f(elem) ? [...acc, elem] : acc, []);
const res = test(x, n => n % 2 === 0);
console.log(res);
You can create an empty array at the start of your function, that you populate while reading your array before returning it.
As you don't want to use filter, I will provide a solution using forEach, and an other using a basic for loop.
let x = ["1", "2", "3"];
function testFor(list, testFct) {
const resultArray = [];
for (let i = 0; i < list.length; i++) {
if (testFct(list[i])) {
resultArray.push(list[i]);
}
}
return resultArray;
}
function testForeach(list, testFct) {
const resultArray = [];
list.forEach((element) => {
if (testFct(element)) {
resultArray.push(element);
}
})
return resultArray;
}
console.log(testFor(x, (el) => el % 2));
console.log(testForeach(x, (el) => el % 2));
But at the end of the day, I don't see why you could not use filter, as it returns a new array anyway.
If you want to do that in a simple way is this -->
let x = ["1", "2", "3"];
function myFunction(element){
if (element == "1" || element == "2"){
return true
}
return false
}
function test(list, myFunction) {
let newList = [];
for (let i = 0; i < list.length; i++) {
let element = list[i];
if (myFunction(element)){
newList.push(element);
}
}
return newList
}
test(x, myFunction)
There is another simpler way using filter -->
let x = ["1", "2", "3"];
function myFunction(element){
if (element == "1" || element == "2"){
return true
}
return false
}
function test(list, myFunction) {
return list.filter(myFunction)
}
test(x, myFunction)
I am challenging myself on a small JS application, its about a few Basketball Teams where i am calculating the average score of three games there are playing.
I am stuck on a basic thing, i don't get it.
First here is the code:
// simple stringbuilder function
function appendStringBuilder(string, tag) {
return document.querySelector(tag).append(string);
}
// function calculates average score of team
function avgScoreCalc(obj) {
const values = Object.values(obj);
let avgSum = 0;
for (var i = 0; i < values.length; i++) {
if (Number.isInteger(values[i])) {
avgSum += values[i];
}
}
avgSum = avgSum / 3;
return Math.round(avgSum);
}
function challenge2(ObjName, teamName, firstGame, secondGame, thirdGame) {
var ObjName = {
teamName: teamName,
firstGame: firstGame,
secondGame: secondGame,
thirdGame: thirdGame,
};
avgScoreCalc(ObjName);
return appendStringBuilder(`${ObjName.teamName}: ${avgScoreCalc(ObjName)} | `, '.code-output-2');
}
// IS UNDEFINED, WHY? <<<<
const TJohn = challenge2('TJohn', 'Team John', 89, 120, 103);
//----------------------------------------------------------
console.log(TJohn); //<<<< 'undefined'
I really just want to save the return of the "challenge2()" function inside a simple var. What am i doing wrong?
Debugging says its undefined.
Thanks for any help.
append returns undefined. You probably meant to do
function appendStringBuilder(string, tag) {
document.querySelector(tag).append(string);
return string;
}
ParentNode.append does return nothing, aka undefined. As you return the result of that call from appendStringBuilder, and return that again to then finally assign it to a variable, it is no surprise that the variable is undefined at the end.
It looks like the signature of challenge2 function having ObjName is not of any use as again you are creating a variable inside with same name. Also other functions you are using inside not having any definition.
Find the updated 'challenge2' function which will work for same. (still can be optimized)
// function calculates average score of team
function avgScoreCalc(obj) {
const values = Object.values(obj);
let avgSum = 0;
for (var i = 0; i < values.length; i++) {
if (Number.isInteger(values[i])) {
avgSum += values[i];
}
}
avgSum = avgSum / 3;
return Math.round(avgSum);
}
function challenge2( teamName, firstGame, secondGame, thirdGame) {
var ObjName = {
teamName: teamName,
firstGame: firstGame,
secondGame: secondGame,
thirdGame: thirdGame,
};
return `${ObjName.teamName} : ${avgScoreCalc(ObjName)}`;
}
const TJohn = challenge2( 'Team John', 89, 120, 103);
//----------------------------------------------------------
console.log(TJohn);
So my array looks like this:
let array = [
{"object1":1},
{"object2":2},
{"object3":3}
];
What I want to do is to check, for example, whether or not "object1" exists. The way I would prefer is pure Javascript.
I am doing this for large chunks of data and so my code needs to be something like this:
if ("opensprint1" in array){
console.log("yes, this is in the array");
} else {
console.log("no, this is not in the array");
};
NOTE: I have tried to use the (in) function in JS and the (hasOwnProperty) and neither has worked.
Any ideas?
if ("opensprint1" in array){
That check for the array keys, so it would work with:
if ("0" in array){
But actually you want to check if some of the array elements got that key:
if(array.some( el => "opensprint1" in el))
You're trying to filter an array of objects. You can pass a custom function into Array.prototype.filter, defining a custom search function. It looks like you want to search based on the existence of keys. If anything is returned, that key exists in the object array.
let array = [{
"object1": 1
},
{
"object2": 2
},
{
"object3": 3
}
];
const filterByKey = (arr, keyName) =>
array.filter(obj => Object.keys(obj).includes(keyName)).length > 0;
console.log(filterByKey(array, 'object1'));
console.log(filterByKey(array, 'object5'));
That is roughly equivalent to:
let array = [{
"object1": 1
},
{
"object2": 2
},
{
"object3": 3
}
];
const filterByKey = (arr, keyName) => {
// iterate each item in the array
for (let i = 0; i < arr.length; i++) {
const objectKeys = Object.keys(arr[i]);
// take the keys of the object
for (let j = 0; j < objectKeys.length; j++) {
// see if any key matches our expected
if(objectKeys[i] === keyName)
return true
}
}
// none did
return false;
}
console.log(filterByKey(array, 'object1'));
console.log(filterByKey(array, 'object5'));
This might help you
let array = [
{"object1":1},
{"object2":2},
{"object3":3}
];
let targetkey = "opensprint1";
let exists = -1;
for(let i = 0; i < array.length; i++) {
let objKeys = Object.keys(array[i]);
exists = objKeys.indexOf(targetkey);
if (exists >= 0) {
break;
}
}
if (exists >= 0) {
console.log("yes, this is in the array");
} else {
console.log("no, this is not in the array");
}
let array = [
{ "object1": 1 },
{ "object2": 2 },
{ "object3": 3 }
];
let checkKey = (key) => {
var found = false;
array.forEach((obj) => {
if (!(obj[key] === undefined)) {
found = true;
array.length = 0;
}
});
return found;
}
console.log(checkKey("object2"));
In this case, I think one of the most efficient way is to do a for and break like:
let array = [
{"object1":1},
{"object2":2},
{"object3":3}
];
exist = false;
for(let i = 0; i<array.length; i++){
if("object1" in array[i]){
exist = true;//<-- We just know the answer we want
break;//<-- then stop the loop
}
}
console.log(exist);
When iteration finds a true case, stops the iteration. We can't perform a break in .map, .filter etc. So the number of iterations are the less possible. I think this is also the case of .some()
I'm sure this has been answered somewhere, but I can't find an answer for this specific problem with JS before ES5.
I need a function like this:
function testAllPossibilities(array, callback) {
// example array = [Obj1,Obj2];
// allPossibilites = [
// [Obj1],
// [Obj2],
// [Obj1,Obj2],
// [Obj2,Obj1]
// ]
for (var i = 0; i < allPossibilites.length; i++) {
callback(allPossibilites[i]);
}
}
I'm looking for all permutations without repetitions for the original input array and its sub-arrays.
function combinate (array) {
const possibilities = new Set();
function lookup (possible) {
if possible.length {
possibilities.add(possible);
}
const p = new Set(possible);
const further = array.filter(el => !p.has(el));
for (let entry of further) {
lookup(possible.concat(entry));
}
}
lookup([]);
return Array.from(possibilities);
}
Do not run this with more than 8 elements.