Returning NULL from an empty array - javascript

I'm currently learning basic web development with JavaScript, taking an online course which corrects code using a bot. I'm trying to implement a function which calculates the average value of an array.
let numbers = [1,2,3]
function average(array){
var total = 0;
var count = 0;
array.forEach(function(item){
total += item;
count++;
})
if (numbers.length > 0){
return total / count;
} else if (numbers = ([])) {
return null
}
}
The code works fine, in practice, but I get an error returned saying 1) defines average such that average([]) returns null, as in if an empty array is sent in, average([]) is supposed to return null I can't figure out how to fix it...

I would make it way more simpler without the counter. You have the total in the length of the array.
I also added a version using array.reduce().
And please, don't use numbers variable inside the function. It makes no sense here. You pass the variable to the function and inside the function you should use the received variable or it will behave incorrectly. Inside the function numbers is called "arr", so use arr all the way.
function average(arr){
if (arr.length === 0) return null;
let total = 0;
arr.forEach(function(item){
total += item;
})
return total / arr.length;
}
// using array.reduce() which makes more sense here
function average2(arr){
if (arr.length === 0) return null;
const total = arr.reduce(function(prev,next){
return prev + next;
});
return total / arr.length;
}
console.log(average([]));
console.log(average([1,2,3]));
console.log(average2([]));
console.log(average2([1,2,3]));

You don't need to test for []. If the array has a length of zero, then it's empty:
let numbers = [1, 2, 3]
function average(array) {
var total = 0;
var count = 0;
// No need to iterate over array if it's empty
if (array.length > 0) {
array.forEach(function(item) {
total += item;
count++;
})
return total / count;
} else {
// If we got here, array.length === 0
return null
}
}
console.log(average(numbers));
numbers = [];
console.log(average(numbers));

In the second case, numbers = ([]) assigns the numbers to [] (which always return true) instead of comparing it. The right way would be using == as follows:
let numbers = [1,2,3]
function average(array){
var total = 0;
var count = 0;
array.forEach(function(item){
total += item;
count++;
})
if (array.length > 0){
return total / count;
} else if (array.length == 0) {
return null
}
}
console.log(average(numbers));
numbers = [];
console.log(average(numbers));
EDIT:
As mentioned in the comment, there is another mistake, where you are using numbers instead of array in the function.

The reason, why your test case fails, is that you define numbers at the top and then reuse it within the function. That way it always returns the same. You should use array within the function instead.
Here a short version of your script to see, what js is capable of.
function average(array){
let total = array.reduce((acc, value) => acc + value, 0);
return array.length > 0 ? total / array.length : null;
}
console.log(average([1,2,3]))
console.log(average([]))

You are using numbers instead of array inside average() method.
It could be possible that your array can be undefined. Try using this
function average(array) {
if (typeof array == "object" && array.length > 0) {
var total = 0;
var count = 0;
array.forEach(function(item) {
total += item;
count++;
});
return total / count;
} else {
return null;
}
}

Related

Problems with finding unique values in regEx

I have a javascript regEx that is supposed to find all values with curly brackets around them eg {} and return a list of the unique values. I thought that it was working perfectly but I found that it doesn't work depending on the sequence of values.
For example: If the target document contains {lorem}{lorem}{ipsem}{ipsem} the script logs what's wanted [lorem, ipsem] but {lorem}{ipsem}{ipsem}{lorem} the script logs [lorem, ipsem,lorem]. What am I doing wrong!?
function getVariables() {
var doc = DocumentApp.getActiveDocument();
var str = doc.getText(); //get the text of the document
var result = str.match(/{.*?}/g).map(function(val) {
return val.replace(/[\])}[{(]/g, "");
//return val.replace(/(^.*\[|\].*$)/g,'');
});
//The purpose of sort_unique is to find one of every value or string represented in an array
function sort_unique(arr) {
if (result.length === 0) return arr;
arr = arr.sort(function(a, b) {
return a * 1 - b * 1;
});
var ret = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (arr[i - 1] !== arr[i]) {
ret.push(arr[i]);
}
}
for (var index = 0; index < ret.length; index++) {
Logger.log(ret[index]);
}
return ret;
}
result = sort_unique(result);
Logger.log("Getting final result for front end....");
Logger.log(result);
return result;
}
I believe part of your problem is the sort method. If you replace
arr = arr.sort(function(a, b) {
return a * 1 - b * 1;
});
with
arr = arr.sort();
Then the function appears to work, at least on my side.
This will run in O(n log n) time. You can do better without sorting, if you store the values you've found so far in a map instead of an array. This would run in linear time.
(Also you'll want to replace if (result.length === 0) return arr; with if (arr.length === 0) return arr; just to make your sort_unique function completely independent of the surrounding function.)
The simplest method would be to use a Set. Store each of the regex matches in a set, then return Array.from(mySet).
var mySet = new Set();
str.match(/{.*?}/g).forEach(function(val) {
mySet.add(val.replace(/[\])}[{(]/g, ""));
});
return Array.from(mySet);
A set's add() function is O(1) so the total running time is O(n) where n is the number of matches in your string. Though, realistically, the regex search will be where most of the processing time occurs.
You check if the subsequent items are the same and those that are not subsequent land in the resulting array.
Check if the found value is in the result, and if not add the match, else, ignore.
Use the code like
function getVariables() {
var doc = DocumentApp.getActiveDocument();
var str = doc.getText(); //get the text of the document
var m, result=[], rx = /{([^{}]*)}/g;
while (m=rx.exec(str)) {
if (result.indexOf(m[1]) == -1) {
result.push(m[1]);
}
}
result.sort(); // If you really want to sort use this
// Logger.log(result); // View the result
}
The /{([^{}]*)}/g regex matches {, then captures into Group 1 zero or more chars other than { and }. So, the value you need is in m[1]. The if (result.indexOf(m[1]) == -1) checks if the value is in result.

Multiplicative Persistence Codewars Challenge

I've been working on a kata from Codewars, the challenge is to write a function, persistence, that takes in a positive parameter num and returns its multiplicative persistence, which is the number of times you must multiply the digits in num until you reach a single digit.
Example:
persistence(39) === 3 // because 3*9 = 27, 2*7 = 14, 1*4=4
// and 4 has only one digit
persistence(999) === 4 // because 9*9*9 = 729, 7*2*9 = 126,
// 1*2*6 = 12, and finally 1*2 = 2
persistence(4) === 0 // because 4 is already a one-digit number
While trying to figure this out I came across a solution online (shown below) and after trying to understand its logic, I couldn't see why the code didn't work
var count = 0;
function persistence(num) {
if (num.toString().length === 1) {
return count;
}
count++;
var mult = 1;
var splitStr = num.toString().split("");
for (var i = 0; i <= splitStr; i++) {
mult *= parseFloat(splitStr[i])
}
return persistence(parseFloat(mult));
}
The output for any single digit number will be 0 which is correct however for any number that is multiple digits, the persistence always logs as 1 and I can't seem to figure out why, any help would be greatly appreciated.
The posted code has quite a few problems.
for (var i = 0; i <= splitStr; i++) {
But splitStr is an array, not a number; i <= splitStr doesn't make sense. It should check against splitStr.length instead of splitStr.
Another problem is that it should use i <, not i <=, else the final splitStr[i] will be undefined.
Another problem is that the count variable is global, so more than one call of persistence will result in inaccurate results. There's no need for a count variable at all. To fix it:
function persistence(num) {
if (num.toString().length === 1) {
return 0;
}
var mult = 1;
var splitStr = num.toString().split("");
for (var i = 0; i < splitStr.length; i++) {
mult *= parseFloat(splitStr[i])
}
return 1 + persistence(parseFloat(mult));
}
console.log(
persistence(999),
persistence(39),
persistence(4)
);
Or, one could avoid the for loop entirely, and use more appropriate array methods:
function persistence(num) {
const str = num.toString();
if (str.length === 1) {
return 0;
}
const nextNum = str.split('').reduce((a, b) => a * b, 1);
return 1 + persistence(nextNum);
}
console.log(
persistence(999),
persistence(39),
persistence(4)
);
or we can use while loop with reduce array method
const persistence=(num)=>{
let splitNumArr=num.toString().split('')
let newList
let count=0
while(splitNumArr.length>1){
newList=splitNumArr.reduce((acc,curr)=>{
return acc*=curr
})
splitNumArr=newList.toString().split('')
count++
}
return count
}
console.log(persistence(39))===3
console.log(persistence(999))===4
console.log(persistence(9))===0

Comparing values between two arrays

I'm trying to set up a function that checks if a word or a text is a palindrome. To do that, it splits the text so that every letter is an element of a new array, it takes rid of the white spaces and it makes the reverse array.
Then it checks if every element of the two arrays, at the same positions, are equal. If not it returns false, if yes it returns true.
Here the function:
function palindrome(str) {
var low = str.toLowerCase();
var newArray = low.split("");
var noSpace = newArray.filter(function(val) {
return val !== " ";
});
var reverse = noSpace.reverse();
function check (a, b) {
console.log(`checking '${a}' against '${b}'`);
var partial;
var result = 1;
for (var i = 0; i < a.length; i++) {
console.log(`comparing '${a[i]}' and '${b[i]}'`);
if (a[i] !== b[i]) {
result = 0;
} else {
partial = 1;
result *= partial;
}
}
return result;
}
var result = check(noSpace, reverse);
if (result == 1) {
return true;
} else {
return false;
}
}
palindrome("r y e");
I don't know what's wrong but it seems that the function keeps on returning a true value no matter what word or text I pass to the function. What is wrong with that?
Your issue seems to be because reverse() changes the actual array as well. So doing
var reverse = noSpace.reverse();
Will reverse noSpace and assign a reference to it on the variable reverse. That is, both arrays will be the same (reversed) array.
To bypass that, I've used .slice() to create a copy of the original array, and then called .reverse() on that new array, ridding you of any conflicts.
Here's a working snippet of what it looks like:
function palindrome(str) {
var str_array = str.toLowerCase().split("");
var no_space = str_array.filter(function(val) {
return val !== " ";
});
// By applying '.slice()', we create a new array
// reference which can then be reversed and assigned
// to the 'reverse' variable
var reverse = no_space.slice().reverse();
function check(a, b) {
var partial;
var result = 1;
for(var i=0; i < a.length; i++) {
if(a[i] !== b[i]) {
// We don't need to keep
// comparing the two, it
// already failed
return 0;
} else {
// I've kept this part even though
// I don't really know what it is
// intended for
partial = 1;
result *= partial;
}
}
return result;
}
return check(no_space, reverse) === 1;
}
console.log(palindrome("a b a"));
console.log(palindrome("r y e"));
The way you have coded for palindrome is way too complicated.
But there is one problem with your code: when you do a reverse() it changes the original array as well.
So you will need to make sure that you copy it via slice().
Also you can directly send a boolean result rather than doing a 1 and 0.
At result *= partial;, 1 * 1 will always equal 1
I didn't correct your code, but here is a optimized solution for you.
function palindrom(string) {
var arr = string.split("");
var lengthToCheck = Math.floor(arr.length / 2);
for (var i = 0; i < lengthToCheck; i++) {
if (arr[i] != arr[arr.length - (1 + i)]) {
return false;
}
}
return true;
}
First I split the array after every charater of the passed String. After that I get the half of the length of the array as it's enough to check just one half.
With the for-loop I compare the first half with the second half. As soon as I found two characters that do not match I return false. In case the whole first half matches the second half of the array, the for-loop will be completed and after that true will be returned.
What's actually happening is .reverse() reverses an array in place, it then stores a reference to that array which is not what you're calling in your check() method.
Simple fix would be to change your if statement:
if (a[i] !== b.reverse()[i])

Javascript How to identify if all elements of one array are present in another

I need to create a function to check if all the letters in the second string of a two string array are present in the first string. The function I wrote seems to work for most of the examples I tried with it but ["hello" , "hey"] returns true despite there not being a y in hello and I don't understand why.
Here's my code:
function mutation(arr) {
arr[0] =arr[0].toUpperCase().split("");
arr[1] =arr[1].toUpperCase().split("");
for(i=0;i<arr[1].length;i++){
if(arr[0].indexOf(arr[1][i])>=0){
return true;
} else {return false;}}}
mutation(["hello", "Hey"]);
You are returning true even if one character is matched ,Try below code it checks if all characters are present or not
function mutation(arr) {
arr[0] = arr[0].toUpperCase().split("");
arr[1] = arr[1].toUpperCase().split("");
var count = 0;
for (i = 0; i < arr[1].length; i++) {
if (arr[0].indexOf(arr[1][i]) >= 0) {
count++;
}
}
return count === arr[1].length
}
mutation(["hello", "Hey"]);
here is one more efficient solution, it works only for lowercase letters.
(function(){
function charCode(str, i){
return str.charCodeAt(i) - 97;
}
function isMutation(a,b){
const aArr = new Uint8Array(26);
const bArr = new Uint8Array(26);
let i=0;
let index = 0;
while(i<a.length){
++aArr[charCode(a, i)];
++i;
}
i = 0;
while(i<b.length){
++bArr[charCode(b, i)];
++i;
}
i = 0;
while(i < 26){
if(!(aArr[i]===0 && bArr[i]===0 || aArr[i]>0 && bArr[i]>0)){
return false
}
++i;
}
return true;
}
console.assert(isMutation('hello', 'oleh') === true);
console.assert(isMutation('hello', 'hey') === false);
})();
you can also compare sum of uniq chars in the both arrays, but in this case you have to add each letters only once.
I would recommend using one of the code solutions suggested by user georg at Remove Duplicates from JavaScript Array.
For example, the function below could be used to sort each array (arr[0] and arr[1]) and remove duplicates.
Credit to user georg at the link above.
function uniq(a) {
return a.sort().filter(function(item, pos, ary) {
return !pos || item != ary[pos - 1];
})
}
Once you have sorted/removed duplicates, you can test to see if the two returned strings are equal or not.
Hello => EHLO, and Hey => EHY
EHLO !== EHY

Search closest next value in javascript array

I have a javascript array like var test = [2,5,8,12,56]; and now I want to search the closest next value of 9. So the output is 12 in this case (and not 8!).
Well here's a simple way to do it:
function getNextVal(arr, val) {
// omit the next line if the array is always sorted:
arr = arr.slice(0).sort(function(a,b){return a-b;});
for (var i=0; i < arr.length; i++)
if (arr[i] >= val)
return arr[i];
// return default value when val > all values in array
}
You don't say what to return if the search value is in the array, so I've assumed you want to return it. If by "closest next value" you meant that it should always return the next number higher than the search value change arr[i] >= val to use > instead of >=.
If you have a large array you probably want some kind of binary sort instead of just going through from the beginning.
Here is what you can try if the array is sorted, you need to tune for for boundry cases, this is just for idea of algorithm...
NUM is input
TEST is your array
INDEX is index variable
For INDEX from 0 .. TEST.SIZE -1
IF NUM > TEXT[INDEX]
RETURN TEXT[INDEX]
A very simple code is given below. Hope this will help you
var test = [2,5,8,12,56];
var key = 9;
var closestNext=1000;
for(var i=0;i<test.length;i++)
{
if(test[i] > key)
{
if(test[i]<closestNext)
{
closestNext = test[i];
}
}
}
alert(closestNext);
​
see the working one here
1 Start by sorting the array, using arr.sort();, just sorts the values in the ascending order (3,6,4,7,1 --> 1,3,4,6,7), then just iterate:
function getNext(inputVal,arr)
{
arr.sort();;
for (var i=0;i<arr.lenght;i++)
{
if (arr[i] >= inputVal)
{
return arr[i];
}
}
throw new Error('Out of range');
}
If you know the array is always going to be sorted or if it is reasonable to sort the array beforehand (e.g. when the array doesn't change very often but you need a lot of retrievals), you can use a binary search on the sorted array.
If the value is not found in the array, the upper bound is returned which indicates the smallest element greater than the given value. This gives O(log n) complexity on average whereas the naive approach (looping over the whole array) gives O(n) complexity on average.
// Binary search
// Adapted from http://jsfromhell.com/array/search
function binarySearch(arr, val, insert) {
var high = arr.length, low = -1, mid;
while (high - low > 1) {
mid = (high + low) >> 1;
if (arr[mid] < val) low = mid;
else high = mid;
}
if (arr[high] == val || insert) {
return high;
} else {
return -1;
}
}
function getClosestNext(arr, val) {
// Get index
var i = binarySearch(arr, val, true);
// Check boundaries
return (i >= 0 && i < arr.length) ? arr[i] : null;
}

Categories

Resources