The elegant way to resolve negative zero in Javascript - javascript

I have to multiply the sign of all elements in an array.
For example 1:
input: [1, 2, 3]
output: 1
Explain: 1 * 1 * 1 = 1
Ex2:
input: [1, -2, 3]
output: -1
Explain: 1 * (-1) * 1 = -1
Ex3:
input: [1, -2, 3, 0]
output: 0
Explain: 1 * (-1) * 1 * 0 = 0
And here is my solution
function cal(A)
{
return A.reduce((total, currentValue) => total * Math.sign(currentValue), 1);
}
However, the output of ex3 cal([1, -2, 3, 0]) is -0.
I've already thought about adding one more condition like this
function cal(A)
{
var total = A.reduce((total, currentValue) => total * Math.sign(currentValue), 1);
if(total === 0)
return 0;
else
return total;
}
And obviously, It looks ugly. Is there a more elegant way to resolve that?

In order to avoid conditional checks and keep the function purely computational you can use the curious rules of -0 to simply add 0 to the result of your reduce() which will have no effect on non-zero results but will have the effect of converting -0 to 0.
function cal(arr) {
return arr.reduce((a, c) => a * Math.sign(c), 1) + 0;
}
console.log(cal([1, 2, 3])); // 1
console.log(cal([1, -2, 3])); // -1
console.log(cal([1, -2, 3, 0])); // 0
See signed zero for more general discussion.

In your example, there is no need to multiply all the values. If there is at least one zero, the result is zero, so there is no need to iterate through the entire array. Example below:
function compute(arr)
{
let result = true;
for (let item of arr) {
if (item === 0) return 0;
if (item < 0) result = !result;
}
return result ? 1 : -1;
}
console.log(compute([1, 2, 3]));
console.log(compute([1, -2, 3]));
console.log(compute([1, -2, 3, 0]));

Related

Odd number of elements in an array

When trying to figure out how to get the median value of an array, I first wanted to figure out if the number of elements in the array is odd or even.
If the number is odd then the console will log the element in the middle.
If there's an even number of elements I want the console to log 'Not an odd number of numbers'.
I'm sure there's an easier way to get the median of an array, but I want to figure that out by myself. Right now I just need help with why the console logs the number 1 in my second test of an array, even though there is an even number of elements in that array? It worked fine in the first and third test but not the second one...why?
function median (arr) {
let sorted = arr.sort((a, b) => a-b)
let midIndex = sorted.splice(0, sorted.length / 2)
if (arr.length %2 != 0) {
return midIndex[midIndex.length-1]
} else {
return "Not an odd number of numbers"
}
}
try {
let result = median([4, 8, 2, 4, 5])
console.log(result) // -> 4
result = median([-5, 1, 5, 2, 4, 1]) // -> 1
console.log(result)
result = median([5, 1, 1, 1, 3, -2, 2, 5, 7, 4, 5, 6]) // -> Not an odd number of numbers
console.log(result)
} catch (e) {
console.error(e.message)
}
Because splice actually changes the length of the original array - it does not create a new array. You can see this by taking a note of the length before calling splice
function median (arr) {
var initLength = arr.length;
let sorted = arr.sort((a, b) => a-b)
let midIndex = sorted.splice(0, sorted.length / 2)
console.log(initLength, arr.length);
if (arr.length %2 != 0) {
return midIndex[midIndex.length-1]
} else {
return "Not an odd number of numbers"
}
}
try {
let result = median([4, 8, 2, 4, 5])
//console.log(result) // -> 4
result = median([-5, 1, 5, 2, 4, 1]) // -> 1
//console.log(result)
result = median([5, 1, 1, 1, 3, -2, 2, 5, 7, 4, 5, 6]) // -> Not an odd number of numbers
//console.log(result)
} catch (e) {
console.error(e.message)
}
So splicing an array of 6 in half gives a length of 3 which is odd. Instead use the original array length and it does what you expected:
function median (arr) {
var initLength = arr.length;
let sorted = arr.sort((a, b) => a-b)
let midIndex = sorted.splice(0, sorted.length / 2)
if (initLength %2 != 0) {
return midIndex[midIndex.length-1]
} else {
return "Not an odd number of numbers"
}
}
try {
let result = median([4, 8, 2, 4, 5])
console.log(result) // -> 4
result = median([-5, 1, 5, 2, 4, 1]) // -> 1
console.log(result)
result = median([5, 1, 1, 1, 3, -2, 2, 5, 7, 4, 5, 6]) // -> Not an odd number of numbers
console.log(result)
} catch (e) {
console.error(e.message)
}

How to select the middle of an array?

I thought I could use
$ [1,2,3,4,5].splice((this.length / 2), 1)[0]
but it gives me
$ 1
I tried
$ [1,2,3,4,5].splice(function() { return this[this.length / 2, 1]})
but it gives me
[ 1, 2, 3, 4, 5 ]
I'm looking for a solution that gives me an integer and for even arrays is the lower of the two, e.g.
[1,2,3,4] givees 2
[1,2,3,4,5] gives 3
[1,2,3,4,5,6] gives 3
[1,2,3,4,5,6,7] gives 4
The issue is with this reference. Try this:
console.log(getMiddle([1,2,3,4]));
console.log(getMiddle([1,2,3,4,5]));
console.log(getMiddle([1,2,3,4,5,6]));
console.log(getMiddle([1,2,3,4,5,6,7]));
function getMiddle(arr){
return arr.splice(Math.floor((arr.length-1) / 2), 1)[0]
}
However, As #jonrsharpe 's comment states, splicing a single-element array from an index to the same index plus one then getting the first value in the result creates a redundant array. A better way to get the middle element would be the following:
console.log(getMiddle([1,2,3,4]));
console.log(getMiddle([1,2,3,4,5]));
console.log(getMiddle([1,2,3,4,5,6]));
console.log(getMiddle([1,2,3,4,5,6,7]));
function getMiddle(arr){
return arr[Math.floor((arr.length - 1) / 2)];
}
You should use something like that.
var data, remaining, hold, result;
data = ["1","2","3","4","5","6","7","8", "9"];
remaining = data.length % 2;
hold = Math.floor(data.length / 2);
result = data[(remaining + hold - 1)];
document.write(result);
You could create a prototype for getting the middle element/s.
Array.prototype.middle = function () {
const
half = this.length >> 1,
offset = 1 - this.length % 2;
return this.slice(half - offset, half + 1);
};
console.log([1, 2, 3, 4, 5].middle()); // [3]
console.log([1, 2, 3, 4, 5, 6].middle()); // [3, 4]
console.log([1, 2, 3, 4].middle()); // [2, 3]
.as-console-wrapper { max-height: 100% !important; top: 0; }
You cannot use this as a parameter because this is supposed to be called inside functions, whereas splice accept as parameters integers. If you really want to use this you may use a prototype. Use Math.floor function since arrays indexes only accept integers, and Math.floor rounds up to the lowest integer (ex: Math.floor(2.5) === 2).
Array.prototype.getMiddle = function () {
return this[Math.floor(this.length/2)]
}
console.log([1,2,3,4,5].getMiddle()) // 3
The above function only works when the length of the array is an odd number, since arrays whose length is an even number do not have "middle" element.
If you want to check if the array has a middle
Array.prototype.getMiddle = function () {
return (this.length % 2) ? this[Math.floor(this.length/2)] : null
}
console.log([1,2,3,4,5].getMiddle()) // 3
console.log([1,2,4,5].getMiddle()) // null
Alternative solutions:
var arr = [1,2,3,4,5]
var middleEl = arr[Math.floor(arr.length/2)]
console.log(middleEl) // 3
If you want to use splice
var arr = [1,2,3,4,5]
var middleEl = arr.splice((arr.length / 2), 1)[0]
console.log(middleEl)
If you want a function
console.log(middleEl([1,2,3,4,5])) //3
console.log(middleEl([1,2,4,5])) //null
function middleEl (arr) {
return (arr.length % 2) ? arr[Math.floor(arr.length/2)] : null
}
let arr = [ 1, 2, 3, 4, 5 ]
let len = arr.length;
let mid = Math.floor(len / 2);
console.log(arr[mid]);
// or
console.log(arr.slice(mid, mid + 1));
or if you want the middle two, you can do mid + 2 using a var that tests to see if the length is even.
Why not simply, assuming a.length > 0:
const a = [1, 2, 3, 4, 5]
console.log(a[(a.length - 1) >> 1])
const b = [1, 2, 3, 4, 5, 6]
console.log(b[(b.length - 1) >> 1])
I think this should be a fairly fast way of doing it. The >> operator is an integer shift, which divides the number by two - ignoring decimals.

A question on JavaScript .reduce method's accumulator

I was working on this problem to create a function using a reduce method that will get the max number in an array.
The instructor's answer is:
const numbers = [1, 2, 3, 4, 4, 5, 1, 3, 4];
const max = getMax(numbers);
console.log(max);
function getMax(array) {
if (array.length === 0) return undefined;
return array.reduce((accumulator, current) => {
return (accumulator > current) ? accumulator : current
});
I tried something like this:
return array.reduce((accumulator, current) => {
if (accumulator < current)
console.log(accumulator, current);
return accumulator = current;
});
I added console.log (accumulator, current) because I wanted to see what's going on with my code. The log shows as follows:
console.log of my code
1 2
2 3
3 4
4 5
1 3
3 4
4
Question 1. I'd like to know why my function didn't give the right output (it returned 4, not the correct output 5). Shouldn't "accumulator" stay 5 when it is assigned as 5 during the loop?
Question 2. Why do I need to return (or add return in front of) array in the function, when there is already a return below the if statement?
You didn't use { ... } after your if statement, so only the first line console.log(...) is happening when the condition is met. The accumlator = current line always happens for each iteration. You must use return when using imperative style if statement. However you can skip return when using functional style expressions, ie (accumulator, current) => accumulator < current ? current : accumulator which says "if accumulator is less than current, return current, else return accumulator".
Consider this decomposed program. When we see max as an independent function, it helps us see precisely the type of function reduce is expecting -
const max = (a = 0, b = 0) =>
a < b // if a is less than b
? b // return b
: a // otherwise return a
const getMax = (numbers = []) =>
numbers.length === 0 // if numbers.length is zero
? undefined // return undefined
: numbers.reduce(max) // otherwise return reduction
console.log(getMax([1, 2, 3, 4, 4, 5, 1, 3, 4]))
// 5
console.log(getMax([]))
// undefined
console.log(getMax())
// undefined
We can see reduce is produces the following computation -
// given
[1, 2, 3, 4, 4, 5, 1, 3, 4]
// starting with the first two
r = max(1, 2)
// then the next number
r = max(r, 3)
// then the next number
r = max(r, 4)
// then the next number
r = max(r, 4)
Or without intermediate r = ... -
max(max(max(max(max(max(max(max(1, 2), 3), 4), 4), 5), 1), 3), 4)
We could write getMax without reduce, if we wanted -
const max = (a = 0, b = 0) =>
a < b
? b
: a
const getMax = (numbers = []) =>
numbers.length === 0 // without any numbers,
? undefined // there can be no max.
: numbers.length === 1 // if we only have one,
? numbers[0] // we already know max.
: max(numbers[0], getMax(numbers.slice(1))) // else
console.log(getMax([1, 2, 3, 4, 4, 5, 1, 3, 4]))
// 5
console.log(getMax([]))
// undefined
console.log(getMax())
// undefined
Or maybe you haven't learned slice yet. You can use an array index, i, to step thru your array -
const max = (a = 0, b = 0) =>
a < b
? b
: a
const getMax = (numbers = [], i = 0) =>
numbers.length === 0 // without any numbers,
? undefined // there can be no max.
: i + 1 >= numbers.length // if the next i is not in bounds,
? numbers[i] // this is the last number
: max(numbers[i], getMax(numbers, i + 1)) // else
console.log(getMax([1, 2, 3, 4, 4, 5, 1, 3, 4]))
// 5
console.log(getMax([]))
// undefined
console.log(getMax())
// undefined
Destructuring assignment can be used as well -
const max = (a = 0, b = 0) =>
a < b
? b
: a
const getMax = ([ num, ...more ] = []) =>
more.length === 0
? num
: max(num, getMax(more))
console.log(getMax([1, 2, 3, 4, 4, 5, 1, 3, 4]))
// 5
console.log(getMax([]))
// undefined
console.log(getMax())
// undefined
This might show you how you can invent your own reduce -
const max = (a = 0, b = 0) =>
a < b
? b
: a
const reduce = (f, a = [], i = 0) =>
a.length === 0 // without any numbers,
? undefined // there can be no reduction.
: i + 1 >= a.length // if the next i is not in bounds,
? a[i] // this is the last element
: f(a[i], reduce(f, a, i + 1)) // else
const getMax = (numbers = []) =>
reduce(max, numbers) // <-- our reduce!
console.log(getMax([1, 2, 3, 4, 4, 5, 1, 3, 4]))
// 5
console.log(getMax([]))
// undefined
console.log(getMax())
// undefined
Try use Math.max method:
const numbers = [1, 2, 3, 4, 4, 5, 1, 3, 4]
numbers.reduce((acc, rec) => Math.max(acc, rec))
//5
or
function max(numbers) {
return list.reduce((acc, rec) => acc > rec ? acc : rec)
}
if you need find max value without Math.max.

How to return half of an array short way?

so I answer this Q but I believe there is another way
the Q is :
Write a function halve that copies the first half of an array. With an odd number of array elements, the middle element should belong to the first half. halve([1, 2, 3, 4]) should return [1, 2].
and ([1, 2, 3, 4, 5]) return [ 1, 2, 3 ].
function halve(x) {
let len = x.length / 2 - 0.5
let xx = x[len]
if (x.length % 2 === 0) {
return x.slice(0, x.length / 2)
} else {
return x.slice(0, xx)
}
}
console.log(halve([1, 2, 3, 4]));
You can use built-in filter function
function halve(x){
return x.filter((i, idx) => idx < Math.floor(x.length / 2))
}
console.log( halve([1, 2, 3, 4]));
function halve(arr){
let half = arr.length / 2 + 0.5;
return arr.slice(0, half)
}
You can compute the length and use .filter() to find out whether the item is an early item, using the fact that the second parameter of .filter() is the index to your advantage.
function halve(x) {
let len = Math.ceil(x.length / 2);
return x.filter((item, index) => index < len);
}
console.log(halve([1, 2, 3, 4]));
console.log(halve([1, 2, 3, 4, 5]));
arr.slice(0, -arr.length / 2);

FInding out the Harshad number

I am being stuck in situation where the problem is defined as:
Harshad/Niven numbers are positive numbers that are divisible by the sum of their digits. All single-digit numbers are Harshad numbers.
For example, 27 is a Harshad number as 2 + 7 = 9, and 9 is a divisor
of 27.
Harshad numbers can occur in consecutive clusters. The numbers 1
through 10 are Harshad numbers. The numbers 132 and 133 are both
Harshad numbers. The numbers 1014, 1015, 1016, 1017 are Harshad
numbers.
Create a function that takes a number and returns an array of two
elements. The first element is the length of the Harshad cluster of
which the number is a part. The second is its order in the cluster.
Examples harshad(5) ➞ [10, 5] // cluster = [1, 2, 3, 4, 5, 6, 7, 8, 9,
10] // The second element should be the layman order in the //
cluster, not the programming index.
harshad(133) ➞ [2, 2] // cluster = [132, 133]
so i have figured out a way to find out all the harshed cluster greater than the number passed as arguement to function whose code is defined below
function track(num) {
let a = num.toString().split("");
let b = a.reduce((a, b) => +a + +b, 0);
if (num % b != 0) {return;}
console.log(num,num%b);
if (num % b == 0) {
num++;
track(num);
}
}
track(1015);
so console.log() gives me 1015,1016 and 1017
cluster as they are greater than 1015 which is passed to the function now how can i check for the numbers smaller than 1015 as 1014 should also be the answer but i just cant write another IF statement after the first IF statement and make it as
function track(num) {
let a = num.toString().split("");
let b = a.reduce((a, b) => +a + +b, 0);
if (num % b != 0) {return;}
console.log(num,num%b);
if (num % b == 0) {
num++;
track(num);
}
if(num%b==0){
num--
track(num)
}
}
track(1015);
as this makes no sense
You could separate the functions, one for checking if a number is a harshad value and another which collects these numbers by decrementing the value and incrementing and collecting valid numbers in an array.
function harshad(value) {
function isHarshad(value) {
return value % Array.from(value.toString(), Number).reduce((a, b) => a + b) === 0;
}
var cluster = [],
left = value,
right = value + 1;
while (isHarshad(left)) cluster.unshift(left--);
while (isHarshad(right)) cluster.push(right++);
return [cluster.length, cluster.indexOf(value) + 1];
}
console.log(harshad(5)); // [10, 5] cluster = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(harshad(133)); // [ 2, 2] cluster = [132, 133]
console.log(harshad(1015)); // [ 4, 2] cluster = [1014, 1015, 1016, 1017]
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try this:
function track(num) {
const isHarshed = n => {
let a = n.toString().split("").reduce((a, b) => (+a) + (+b), 0);
return n % a === 0;
};
if (!isHarshed(num)) {return false;}
let index = 1;
let count = 1;
let n = num - 1;
while(isHarshed(n)) {
count++;
index++;
n--;
}
n = num + 1;
while(isHarshed(n)) {
count++;
n++;
}
return [count, index];
}
console.log(track(5));
console.log(track(133));

Categories