Negating Callback Function Return Value - javascript

Straightforward question:
Can I negate a callback function that returns true or false o an array.filter() statement? e.g.
//the callback function
function isEven(element, index, array){
if (index%2 == 0 || index == 0){
return true;
}
return false;
}
//what i want to do with that function
//arr[i] is a string
var evens = arr[i].split("").filter(isEven); //works
var odds = arr[i].split("").filter(!isEven); // doesn't work
the above line gives me the error TypeError: false is not a function
Question with some background:
I'm taking on some Hackerrank challenges and i've come across an exercise that requires to take a string and process it, so the output is: The characters with even index values make a new string and the characters in odd index positions make another string , 0 counts as even.
Input
airplane
Output
evens = 'arln'
odds = 'ipae'
I have already solved it by looping through the string, evaluating the index and then pushing the value to the correspondent new array (which i later convert to a string), but it has occurred to me i could be done in a more functional way, using the Array.prototype.filter() function.
now I create a new function that evaluates whether the index number is even or not, and I'd like to use that same function to fill both arrays (evens and odds), like this (now you can refer to the straightforward question part):
var evens = arr[i].split("").filter(isEven); //works
var odds = arr[i].split("").filter(!isEven); // doesn't work

The simplest way to do this would be to just pass an anonymous function which returns the negated result of isEven.
var evens = arr[i].split("").filter(function(el, index, array) {
return !isEven(el, index, array);
});
But you could take this a step further and write a not function which essentially generates the anonymous function for you. Here's an example of such a function.
var input = [0, 1, 2, 3, 4, 5];
function isEven(value) {
return value % 2 === 0;
}
function not(f) {
return function() {
return !f.apply(null, arguments);
}
}
var output = input.filter(not(isEven));
console.log(output);
If you're in an environment that supports rest parameters then you could write your not function like this.
var input = [0, 1, 2, 3, 4, 5];
function isEven(value) {
return value % 2 === 0;
}
function not(f) {
return function(...args) {
return !f.apply(null, args);
}
}
var output = input.filter(not(isEven));
console.log(output);

You would need to pass in an anonymous function and then negate isEven in there:
var odds = arr[i].split("").filter(function(a, index, array) {
return !isEven(a, index, array);
});
Simple Example:
Working Example
function isEven(n) {
return n % 2 === 0;
}
var arr = [0,1,2,3,4,5,6,7,8,9];
var a = arr.filter(isEven);
var b = arr.filter(function(a) {
return !isEven(a);
});

The solution I use is something like this:
var numbers = [0,1,2,3,4,5];
var evens = [];
var odds = [];
function isEvenOrNot(getEven) {
return function(num) {
if (num % 2 == 0 || num == 0){
return true;
}
return false;
}
}
evens = numbers.filter(isEvenOrNot(true));
odds = numbers.filter(isEvenOrNot(false));
console.log(evens); // [0,2,4]
console.log(odds); // [1,3,5]

Related

Implement function and return new list js

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)

Pushing the values into an array results in undefined variables but works inside a function

I'm trying to only push the values in the 'eachNumber' array with the indexes from the 'indexes' variable inside the 'appearMost' array, but for some reason it returns an array with undefined values:
var indexes = [1,2];
var appearMost = [];
var eachNumber = [4, 7, 9, 8];
indexes.map(function(c) { appearMost.push(eachNumber[c]) }); // should return [7,9]
The result of appearMost should be [7,9].
Strange, because I've built a function that returns the number appearing most frequently in an array which relies on the above line that doesn't seem to work. For example:
mostAppearing([5,5,2,2,1]); // correctly returns 5
mostAppearing([3,4,1,6,10]); // correctly returns -1
mostAppearing([4,7,7,7,9,9,8]); // correctly returns 7
mostAppearing([4,7,7,9,7,9,9,8]); // correctly returns 9
And the function has the code:
function mostAppearing(arr) { // e.g. var arr = [4,7,7,9,7,9,9,8];
var eachNumber = Array.from(new Set(arr)); // [4, 7, 9, 8];
if (arr.length == eachNumber.length) {
return -1;
} else {
var counts = eachNumber.map(function(c){ return arr.filter(function(el){ return el==c }).length }); // [1, 3, 3, 1];
var maxVolume = Math.max(...counts); // 3
var volVolume = counts.filter((c) => c == maxVolume).length; // 2
if (volVolume == 1) {
return arr[maxVolume];
} else {
var indexes = counts.reduce((a, c, i) => (c === maxVolume) ? a.concat(i) : a, []); // [1,2]
var appearMost = [];
indexes.map(function(c) { appearMost.push(eachNumber[c]) }); // relies on this line
return Math.max(...appearMost);
}
}
}
Can anyone explain (1) why undefined values are the result rather than [7,9], and (2) how my function works correctly? It should fail. Thanks for any help here.
The value of appearMost is updated correctly.
var indexes = [1,2];
var appearMost = [];
var eachNumber = [4, 7, 9, 8];
indexes.map(function(c) { appearMost.push(eachNumber[c]) })
console.log(appearMost)
I believe you expected the return value of the map function to be 7,9 instead of the value inside appearMost.
The map itself will not return a value as you did not use return inside your function.
A better practice would be having the map function return array instead of mutating an existing one:
appearMost = indexes.map(function(c) { return eachNumber[c] })
Update your code as below and you can get desired result. Here count holds value as object { data: d, count: d.length }. then max will hold maximum repeated value count. Then filtered counts object for maximum repeated value and selected only data to map in appearMost object. Returned max value from appearMost.
function mostAppearing(arr) { // e.g. var arr = [4,7,7,9,7,9,9,8];
var eachNumber = Array.from(new Set(arr)); // [4, 7, 9, 8];
if (arr.length == eachNumber.length) {
return -1;
} else {
var counts = eachNumber.map(function(c) {
var d = arr.filter(el => el == c);
return { data: d, count: d.length }
});
var max = Math.max(...counts.map(x => x.count));
var appearMost = counts.filter(c => c.count == max).map(x => x.data[0]);
return Math.max(...appearMost);
}
}
console.log(mostAppearing([5,5,2,2,1])); // correctly returns 5
console.log(mostAppearing([3,4,1,6,10])); // correctly returns -1
console.log(mostAppearing([4,7,7,7,9,9,8])); // correctly returns 7
console.log(mostAppearing([4,7,7,9,7,9,9,8])); // correctly returns 9
To filter through the entire array for each item is probably not the most efficient.
You can go through the array once with a reduce creating a Map that has the array item as key and the amount it occurs as value.
Then reduce it once more getting the most occurring and highest number. I put the guard of empty array and edge case of all numbers only appearing once (return -1 in both cases) in a seperate function:
const highestMostAppearing = (arr) =>
[
...arr
.reduce(
(result, number) =>
result.set(number, (result.get(number) || 0) + 1),
new Map(),
)
.entries(),//Map where key is the number and value is the amount of time it occurs
].reduce(//this will error with empty array but mostAppearing will guard for that
//result is highestNumber and mostAppeared so far
// item is the number and how many times it appeared
([highestNumber, mostAppeared], [number, appears]) =>
appears > mostAppeared//current item appeared more often than the most appeared so far
? [number, appears]//return current number and how many times it appeared
//next line checks if current number appeared the same times as highest so far
// and checks if current number is higher than the highest appeared number
: appears === mostAppeared && highestNumber < number
? [number, appears]//replace result with current item values
: [highestNumber, mostAppeared],//return previous result (is most appearing and highest)
);
const mostAppearing = (arr) => {
if (arr.length === 0) return -1;//do not call highestMostAppearing with empty array
const [highest, appearing] = highestMostAppearing(arr);
if (appearing === 1) return -1;//all numbers appear only once (expensive op here)
return highest;//return most appearing highest nubmber
};
console.log('=======', mostAppearing([5, 5, 2, 2, 1]));
console.log('=======', mostAppearing([]));

recursively putting array elements in their own array

I'm trying to create a function that puts each array element in its own array, recursively.
I think my base case is correct, but my recursive call doesn't appear to be working. any insight?
function ownList(arr){
if (arr.length === 1) {
arr[0] = [arr[0]];
return;
} else {
return arr[0].concat(ownList(arr.slice(1)));
}
}
var arr = [1,2,3]
console.log(ownList(arr))// returns []
//should return [[1],[2],[3]]
Here I'm trying to put each pair in it's own list (recursive only). This code below is correct (update)
function ownListPair(arr){
if (arr.length === 0)
return arr;
else if(arr.length === 1)
return [[arr[0], 0]];
else
return [[arr[0], arr[1]]].concat(ownListPair(arr.slice(2)));
}
// var arr = [3,6,8,1,5]
var arr = [2,7,8,3,1,4]
//returns [ [ 2, 7 ], [ 8, 3 ], [ 1, 4 ]]
console.log(ownListPair(arr))
I prefer this solution for several reasons:
function ownList(a) {
return a.length == 0
? []
: [[a[0]]].concat(ownList(a.slice(1)))
}
It's shorter and more concise
It works for empty arrays as well
The actual wrapping happens only once in the last line. Treating length == 1 separately -- as suggested by others -- is not necessary.
It would more appropriate to make a length of 0 be the null case. Then you just have to get the brackets right. The thing on the left side of the concat should be an array consisting of the array containing the first element.
function ownList(arr) {
return arr.length ? [[arr[0]]].concat(ownList(arr.slice(1))) : [];
}
Here's an alternative, take your pick:
function ownList(arr) {
return arr.length ? [[arr.shift()]] . concat(ownList(arr)) : [];
}
Using a bit of ES6 magic for readability:
function ownList([head, ...tail]) {
return head === undefined ? [] : [[head]] . concat(ownList(tail));
}
Here the [head, ...tail] is using parameter destructuring which pulls the argument apart into its first element (head) and an array of remaining ones (tail).
Instead of concat you could also use the array constructor:
function ownList([head, ...tail]) {
return head === undefined ? [] : Array([head], ...ownList(tail));
}
I think your basic assumption is wrong. What you need to do is check if each item in the array is an array, if not just add the item to the new array, if so have the function run itself on the array item.
That is recursion.
This code does that kind of recursion...
function ownList(arr)
{
var newArr = [];
var length = arr.length;
for (var i = 0; i < length; i++) {
if (typeof(arr[i]) === 'object') {
newArr.push(ownList(arr[i]));
continue;
}
newArr.push([arr[i]]);
}
return newArr;
}
var arr = [1, 2, 3];
console.log(ownList(arr));
Would something like this work:
var arr = [1, 2, 3, ["a", "b", "c", ["str"]]],
result = [];
function flatten(input){
input.forEach(function(el){
if(Array.isArray(el)){
flatten(el)
}else{
result.push([el]);
}
});
}
flatten(arr);
console.log(JSON.stringify(result));
//[[1],[2],[3],["a"],["b"],["c"],["str"]]
JSBIN
Edit:
var result = [];
function flatten(input){
if (input.length === 0){
console.log( "result", result ); //[[1],[2],[3],["a"],["b"],["c"],["str"]]
return;
}
//if zeroth el of input !array, push to result
if (!Array.isArray(input[0])){
result.push(input.splice(0, 1));
flatten(input);
}else{
flatten(input[0]); //else, give input[0] back to flatten
}
}
window.onload = function(){
var arr = [1, 2, 3, ["a", "b", "c", ["str"]]];
flatten(arr);
}
JSBIN
After struggling through this today, turns out that this works :)
function ownList(arr){
//base case:
if (arr.length === 1) {
return [arr];
}
//recurse
//have to do two brackets here --> (arr.slice(0,1)) since length > 1
return [arr.slice(0,1)].concat(ownList(arr.slice(1)));
}
var arr = [1,2,3]
console.log(ownList(arr))// returns [[1],[2],[3]]

javascript: remove values from an array that are in a second array

Using pure javascript, starting with an array, I would like to return and array by removing values that match any value in a second array.
I have solved this problem, but I believe with more code than is really necessary.
I am hoping for a more concise or elegant solution using only javascript.
function removeValues(arr){
array = arguments[0];
args = Array.prototype.slice.call(arguments);
len = arguments.length;
filtered = array.filter(function(n){
x = true;
for (var i = 1; i < len; i++) {
if (n == args[i]) { x = false; }
}
return x;
});
return filtered;
}
removeValues([1,2,3,1,2,3],2,3);
Should use a function that removes values from the first argument (an array) using values in one or more additional arguments.
When you're working with the filter function is not necessary to use loops because you're already in a loop. After converting the arguments into an array with [].slice.call(arguments), you could use indexOf that is responsible for returning the position of a value in an array, if a value is not exists, this returns -1, so we will take all the results that are -1
Your code could be reduced as well:
function removeValues(arr){
return arr.filter(function(val){
return [].slice.call(removeValues.arguments).slice(1).indexOf(val) === -1
})
}
console.log(removeValues([1,2,3,1,2,3],2,3))
ES6 Method: Using Rest parameters and Arrow Functions
var removeValues = (arr, ...values) => arr.filter(val => values.indexOf(val) === -1)
Try this instead...
function removeValues(){
var args = Array.prototype.slice.call(arguments).slice(1);
return arguments[0].filter(function(value) {
return args.indexOf(value) === -1;
});
}
removeValues([1, 2, 3, 1, 2, 3], 2, 3);
It does the exact same thing, but tidies it slightly.
Try like this:
var array1 = [ 1, 2, 3, 4, 5 ];
var array2 = [ 2, 3 ];
var result = array1.filter( function ( elem ) {
return array2.indexOf( elem ) === -1;
});
See example: Running code

How use forEach to make decisions

i have the following array:
var array = [10, 15, 20];
and i have also this variable called N:
var N = 20;
I and make a loop and check all the itens inside, the N must be greater them all numbers so i can call functionA, if is not, i want call other function
array.forEach(function (item) {
if (N > All) {
callFunctionA();
} else if (N > onlySomes) {
callFunctionB();
}
});
But the problem is that if the first number of the array is smaller them the number, the function will be called, and i want check all the numbers.. This is possible???
You can use a boolean and set it false if a number is larger than N. Similarly use a second boolean to check if there is at least one number which is smaller than N.
var allAreSmaller = true;
var someAreSmaller = false;
array.forEach(function (item) {
if (N <= item) {
allAreSmaller = false;
}
if (N > item)
someAreSmaller = true;
});
if (allAreSmaller)
callFunctionA();
else if (someAreSmaller)
callFunctionB()
It's unclear if you only want the function to be called once in the event one or more of the numbers in the array is greater N.
If you want it to be called EACH TIME it finds a number in the array greater than N, what you have is fine.
If you only want to determine if the function should be called or not, and CALL IT ONCE, replace your function call with a boolean variable that's set to false when N is defined, and set it to true inside the if statement. It won't matter if multiple iterations set it to true.
After the loop, check if hte boolean is true, and if so call the funciton.
Example:
var array = [10, 15, 20];
var fireCallback = false;
var N = 20;
array.forEach(function (item) {
if (N > item) {
fireCallback = true;
//callFunction();
}
});
if (fireCallback) {
callFunction();
}
Another approach you could try (if you only want it to fire once) is finding the largest number in the array using Math.max:
var largest = Math.max.apply(Math, array); // 306
Then checking if the largest number is bigger than N
if(largest >= N) {
callFunction();
}
If that is all you are doing in your loop, you could use a filter (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter):
var areAllSmaller = array.filter(function(_item){ return _item > N}).length <= 0; // true or false
Tenta aê!
You can use Array.every() and Array.some()
var flag1 = array.every(function (item) {
return N > item;
});
var flag2 = array.some(function (item) {
return N > item;
});
if (flag1) {
callFunctionA();
}
if (flag2) {
callFunctionB();
}

Categories