Multiplying an array with a single value by a number? - javascript

Why does JavaScript allow you to multiply arrays with a single numeric value by another numeric value or by another array with a single numeric value?:
[3] * 3;
// 9
[3] * 2;
// 6
[3] * [3];
// 9
[1, 2] * 2
// NaN
I would expect NaN to be returned every time but as my experiments in Chrome have demonstrated this is not the case.
Is this the expected behavior? Does this behavior make sense? If so, why?

[3] * 3;
The following steps are taken:
array is converted to a string [3] => "3"
the string is converted to a number Number("3") => 3
3 * 3 gives 9
Similarly, for [1, 2] * 2:
array is converted to a string [1, 2] => ""1,2"
the string is converted to a number Number("1,2") => NaN
NaN * 3 gives NaN
For ECMA freaks among us ;) start here and follow the path multiplicative operator => ToNumber => ToPrimitive => [[DefaultValue]](number) => valueOf => toString

What is happening here is that [3] is being coerced into a String type, which is just "3". Then in "3" * 3 the String value "3" gets converted into the number 3. You finally end up with 3 * 3, which evaluates to 9.
You can see this behavior if you do [3].toString() * 3 or "3" * 3, which also gives you 9,
So the following steps happen:
[3] * 3
[3].toString() * 3
"3" * 3
Number("3") * 3
3 * 3
9
In the case [1, 2], you eventually end up with "1, 2". But Number("1, 2") results in a NaN and a number multiplied with a NaN results in NaN.

http://ecma-international.org/ecma-262/5.1/#sec-15.4.4.2
Since Array.prototype.valueOf is not available, it falls back to using the String version (as per http://ecma-international.org/ecma-262/5.1/#sec-9.9)
The toString renders an array with a single element as the value of the element (so [3].toString() is 3)

//Say to increase each element by 10%, we may use the following:
let arr=[];
let array = [100, 200, 500, 1000];
array.forEach((p,i)=>arr[i]=(p*110/100));
console.log(arr);
//expected [110,220,550,1100]

Related

JS - How to calculate all possible +, -, ÷, × math operation for 4 specific numbers?

Let's say I have these four numbers: [1, 5, 3, 8].
And I want to check which of the math operations equals to 10. Also the order of the numbers matters, meaning the first number should always be the first index in the array and so on.
For example:
(1 + 5) ÷ 3 + 8
1 x 5 - 3 + 8
How can I achieve this?
Honestly, I have no idea how I should approach this. So any help would be greatly appreciated.
We create an Expr class to represent expressions, which consist of two operands (which may be simple numbers or other expressions) and an operator. This provides convenience functions for evaluating the expression and outputting the expression as a string.
In the simple case, where there are only two expressions x and y, we return all possible combinations x+y, x-y, x*y, x÷y.
Then, when there are more expression operands, we find (all combinations of (x operator y) with ...z), and (x operator (all combinations of y with ...z))
class Op {
constructor(symbol,func) { this.symbol = symbol; this.func = func; }
invoke(x,y) { return this.func(
x instanceof Expr ? x.eval() : x,
y instanceof Expr ? y.eval() : y)
}
toString() { return this.symbol }
}
const ops = [
new Op('+', (x,y)=>x+y), new Op('-', (x,y)=>x-y),
new Op('*', (x,y)=>x*y), new Op('÷', (x,y)=>x/y),
]
class Expr {
constructor(x,op,y){ this.x=x; this.op=op; this.y=y }
eval() { return this.op.invoke(this.x, this.y) }
toString() { return `(${this.x}${this.op}${this.y})` }
}
const f = (x,y,...z) =>
!z.length ? ops.map(op=>new Expr(x,op,y)) :
[...f(x,y).flatMap(expr=>f(expr, ...z)),
...f(y,...z).flatMap(expr=>f(x, expr))]
// show all combinations
console.log(f(...[1, 5, 3, 8]).map(i=>i.toString()))
// show only combinations that evaluate to 10
console.log(f(...[1, 5, 3, 8]).filter(i=>i.eval()===10).map(i=>i.toString()))
Construct the set of possible results for each contiguous range of the input values.
If a range has a single number, then the only mathematical expression constructible is the number itself.
Otherwise, if you have a range a[i], a[i+1], ..., a[j] then for each k from i+1 to j, find the set of possible results constructible from a[i], a[i+1], ..., a[k-1], and the set of possible results constructible from a[k], a[k+1], ..., a[j], then combine them using +, -, * and / in every possible combination.
For example, for [1, 5, 3, 8] there's three ways of breaking up the sequence:
[1] [5, 3, 8]
[1, 5], [3, 8]
[1, 5, 3], [8]
Working through just the second line: [1, 5] can produce {1+5, 1-5, 1*5, 1/5} and [3, 8] can produce {3+8, 3-8, 3*8, 3/8}, so this gives {(1+5)+(3+8), (1+5)-(3+8), (1+5)*(3+8), (1+5)/(3+8), (1-5)+(3+8), (1-5)-(3+8), ..., (1/5)+(3/8), (1/5)-(3/8), (1/5)*(3/8), (1/5)/(3/8)}.
This is workable as-is if you only have 4 input numbers, but if you have more then you will find you can memoize or use dynamic programming to avoid computing the same things repeatedly. It will always be exponential time though, since the range of constructible values grows exponentially.

What does parameter in Array toString do?

I saw a code like this. I looked up MDN but there's no mention about toString having parameters. What does 3 do inside n.toString(3)?
function solution(n) {
n = n.toString(3).split('').reverse().join('')
return parseInt(n, 3)
}
This is Number.prototype.toString(), not Array.prototype.toString(). It takes radix as an optional parameter.
An integer in the range 2 through 36 specifying the base to use for representing numeric values.
Your code converts n to base-3. If you want to convert a number to binary, you'd do n.toString(2)
const n = 16,
bases = [2, 3, 4, 10, 16]
console.log(
bases.map(radix => n.toString(radix))
)
It is an optional parameter when converting number to string:
Optional. Which base to use for representing a numeric value. Must be
an integer between 2 and 36. 2 - The number will show as a binary
value 8 - The number will show as an octal value 16 - The number will
show as an hexadecimal value
This is the Radix (a number represent base-3 numeral system or any other numeral systems). For example if you put 2 instead of 3 this function convert the result into the binary system.
you can put from 2 - 36 in it as the parameter.And the default value is 10 and this parameter is completely optional.
test this one:
var x = 10;
x.toString(2); // output: "1010"

Make choice from array semi-randomly based on string

I have this problem. I'd like to make random a choice from array [1,2,3,4] based on arbitrary 6 letter string in such way that this choice is always same if string is same.
So if i have string 'dogdog' function would return always '3' for example, but '4' for 'bigcat' etc.
I think the solution might be first hashing the string. How could one convert hash string into choice from array?
You can calculate a hash from a string and take the array item at [hash % array.length]. An example with the DJB hashfunc (see http://www.cse.yorku.ca/~oz/hash.html for more):
function djbHash(s) {
let hash = 5381;
for (let c of s) {
hash = hash * 33 + c.charCodeAt(0);
}
return hash;
}
function mapToValues(s, values) {
return values[djbHash(s) % values.length];
}
console.log(mapToValues('dogdog', [1, 2, 3, 4]));
console.log(mapToValues('bigcat', [1, 2, 3, 4]));
A really simple hash function:
Change each letter of your word by a number (a is 1, b is 2, etc.). Let's call w the whole word changed in a number.
Calculate i = w mod 4. i will be a number between 0 and 3. Add 1 to it.
Congrats, you can now associate any word to a "random" number between 1 and 4. You can of course replace 4 by any other number to associate each world to a random number in any range.

Why is an array with only 1 numerical item usable with some arithmetic?

I don't understand why an empty array, or an array with only 1 "numerical" value can be used in certain calculations.
[] * [] === 0 //true
[2] * [2] === 4 //true
["2"] * ["2"] === 4 //true
However, it does not seem that is always the case with every operator.
[2] + [1] === 3 // false, actual result is "21"
[2] - [1] === 1 // true
I also checked these, and saw expected results:
[4] === "4" // false
[4] === 4 // false
In most cases, I would expect NaN in any of these mathematical operations. What is it about JavaScript arrays that allows this? Is it because the array toString method is being used somehow internally?
The arrays are coerced into strings, such that [] == "", [1] == "1", and [1, 2] == "1,2".
When you do certain mathematical operations on strings, they are coerced into Number types.
For example, when you do [2] * [2] it becomes "2" * "2" which becomes 2 * 2. You can even mix types and do [2] * 2 or "2" * [2].
When you try to use +, it attempts to concatenate the strings instead of coercing the strings to numbers. That's why "2" + "2", "2" + [2], and [2] + [2] each give you "22".
The same ol'problem of having + as string concatenation operator.
An array in Javascript is an Object. When you try to coerce objects into primitive values, there is an order to follow:
<obj>.toString()
<obj>.toNumber()
<obj>.toBoolean()
If you're using the * operator, coerce into string is not possible, so the interpreter goes to toNumber().
When you're using the + operator, the first attempt will be toString(). Since + is also the string concat operator, then the expression will be evaluated as an array.
Yeah, javascript can get ugly.
Edit:
Some further details can be found here.
In regards to +, + is a coercive operator on Strings, irrespective of it being the lvalue and rvalue, I'll quote the specification here:
If Type(lprim) is String or Type(rprim) is String, then Return the
String that is the result of concatenating ToString(lprim) followed by
ToString(rprim)
Multiplication however doesn't follow the same rules, it does however apply ToNumber, which as you've noticed, casts a [Number] to -> Number (+ does this as well, but it is a later step in the operator's definition)
Your last statement is answered by two rules of the SameValue algorithm defined in the spec, namely, if two values are of a different type, return false, alternatively, if two objects refer to the ptr, return true. Thus
a = [1];
b = [1];
a === b; // false
When you do mathematical operations on the arrays JavaScript converts them to the strings. But string doesn't have multiply operator and then JavaScript converts them to the numbers because you try to multiply them:
[] * [] === 0 // [] -> '' -> 0, result is 0 * 0
[2] * [2] === 4 // [2] -> '2' -> 2, result is 4 * 4
["2"] * ["2"] === 4 // ["2"] -> '2' -> 2, result is 4 * 4
String has a + operator:
[2] + [1] === 3 // '2' + '1', result is "21"
but doesn't have a - operator, therefore JavaScript converts them to the numbers too:
[2] - [1] === 1 // true, because they are numbers
=== it's strict equality, means that the objects being compared must have the same type.
[4] === "4" // false, you are comparing array with a string
[4] === 4 // false, you are comparing array with a number

Math.random in regards to arrays

I am confused about how arrays work in tandem with functions like Math.random(). Since the Math.random() function selects a number greater than or equal to 0 and less than 1, what specific number is assigned to each variable in an array? For example, in the code below, what number would have to be selected to print out 1? What number would have to be selected to print out jaguar?
var examples= [1, 2, 3, 56, "foxy", 9999, "jaguar", 5.4, "caveman"];
var example= examples[Math.round(Math.random() * (examples.length-1))];
console.log(example);
Is each element in an array assigned a position number equal to x/n (x being the position number relative to the first element and n being the number of elements)? Since examples has 9 elements, would 1 be at position 1/9 and would 9999 be at position 6/9?
Math.round() vs. Math.floor()
The first thing to note: Math.round() is never the right function to use when you're dealing with a value returned by Math.random(). It should be Math.floor() instead, and then you don't need that -1 correction on the length. This is because Math.random() returns a value that is >= 0 and < 1.
This is a bit tricky, so let's take a specific example: an array with three elements. As vihan1086's excellent answer explains, the elements of this array are numbered 0, 1, and 2. To select a random element from this array, you want an equal chance of getting any one of those three values.
Let's see how that works out with Math.round( Math.random() * array.length - 1 ). The array length is 3, so we will multiply Math.random() by 2. Now we have a value n that is >= 0 and < 2. We round that number to the nearest integer:
If n is >= 0 and < .5, it rounds to 0.
If n is >= .5 and < 1.5, it rounds to 1.
If n is >= 1.5 and < 2, it rounds to 2.
So far so good. We have a chance of getting any of the three values we need, 0, 1, or 2. But what are the chances?
Look closely at those ranges. The middle range (.5 up to 1.5) is twice as long as the other two ranges (0 up to .5, and 1.5 up to 2). Instead of an equal chance for any of the three index values, we have a 25% chance of getting 0, a 50% chance of getting 1, and a 25% chance of 2. Oops.
Instead, we need to multiply the Math.random() result by the entire array length of 3, so n is >= 0 and < 3, and then floor that result: Math.floor( Math.random() * array.length ) It works like this:
If n is >= 0 and < 1, it floors to 0.
If n is >= 1 and < 2, it floors to 1.
If n is >= 2 and < 3, it floors to 2.
Now we clearly have an equal chance of hitting any of the three values 0, 1, or 2, because each of those ranges is the same length.
Keeping it simple
Here is a recommendation: don't write all this code in one expression. Break it up into simple functions that are self-explanatory and make sense. Here's how I like to do this particular task (picking a random element from an array):
// Return a random integer in the range 0 through n - 1
function randomInt( n ) {
return Math.floor( Math.random() * n );
}
// Return a random element from an array
function randomElement( array ) {
return array[ randomInt(array.length) ];
}
Then the rest of the code is straightforward:
var examples = [ 1, 2, 3, 56, "foxy", 9999, "jaguar", 5.4, "caveman" ];
var example = randomElement( examples );
console.log( example );
See how much simpler it is this way? Now you don't have to do that math calculation every time you want to get a random element from an array, you can simply call randomElement(array).
They're is quite a bit happening so I'll break it up:
Math.random
You got the first part right. Math.random will generate a number >= 0 and < 1. Math.random can return 0 but chances are almost 0 I think it's like 10^{-16} (you are 10 billion times more likely to get struck by lightning). This will make a number such as:
0.6687583869788796
Let's stop there for a second
Arrays and their indexes
Each item in an array has an index or position. This ranges from 0 - infinity. In JavaScript, arrays start at zero, not one. Here's a chart:
[ 'foo', 'bar', 'baz' ]
Now the indexes are as following:
name | index
-----|------
foo | 0
bar | 1
baz | 2
To get an item from it's index, use []:
fooBarBazArray[0]; // foo
fooBarBazArray[2]; // baz
Array length
Now the array length won't be the same as the largest index. It will be the length as if we counted it. So the above array will return 3. Each array has a length property which contains it's length:
['foo', 'bar', 'baz'].length; // Is 3
More Random Math
Now let's take a look at this randomizing thing:
Math.round(Math.random() * (mathematics.length-1))
They're is a lot going on. Let's break it down:
Math.random()
So first we generate a random number.
* mathematics.length - 1
The goal of this random is to generate a random array index. We need to subtract 1 from the length to get the highest index.
First Part conclusions
This now gives us a number ranging from 0 - max array index. On the sample array I showed earlier:
Math.random() * (['foo', 'bar', 'baz'].length - 1)
Now they're is a little problem:
This code makes a random number between 0 and the length. That means the -1 shouldn't be there. Let's fix this code:
Math.random() * ['foo', 'bar', 'baz'].length
Running this code, I get:
2.1972009977325797
1.0244733088184148
0.1671080442611128
2.0442249791231006
1.8239217158406973
Finally
To get out random index, we have to make this from an ugly decimal to a nice integer: Math.floor will basically truncate the decimal off.
Math.floor results:
2
0
2
1
2
We can put this code in the [] to select an item in the array at the random index.
More Information / Sources
Random Numbers
More solutions
You're looking at simple multiplication, and a bug in your code. It should reference the array 'examples' that you are selecting from, instead of some thing you haven't mentioned called 'mathematics':
var example = examples[Math.round(Math.random() * (examples.length-1))];
^^
Then you're just multiplying a random number by the number of things in the array. So the maximum random number is 1 and if there are 50 things in your array you multiply the random number by 50, and now the maximum random number is 50.
And all the smaller random numbers (0 to 1) are also scaled 50x and now spread from (0 to 50) with roughly the same randomness to them. Then you round it to the nearest whole number, which is a random index into your array from 1 to n, and you can do element[thatnumber] to pick it out.
Full examples:
Math.random() returns numbers between 0 and 1 (it can return 0 but chances of that are incredibly small):
Math.random()
0.11506261994225964
Math.random()
0.5607304393516861
Math.random()
0.5050221864582
Math.random()
0.4070177578793308
Math.random()
0.6352060229006462
Multiply those numbers by something to scale them up; 1 x 10 = 10 and so Math.random() * 10 = random numbers between 0 and 10.
Math.random() *n returns numbers between 0 and n:
Math.random() * 10
2.6186012867183326
Math.random() * 10
5.616868671026196
Math.random() * 10
0.7765205189156167
Math.random() * 10
6.299650241067698
Then Math.round(number) knocks the decimals off and leaves the nearest whole number between 1 and 10:
Math.round(Math.random() * 10)
5
Then you select that numbered element:
examples[ Math.round(Math.random() * 10) ];
And you use .length-1 because indexing counts from 0 and finishes at length-1, (see #vihan1086's explanation which has lots about array indexing).
This approach is not very good at being random - particularly it's much less likely to pick the first and last elements. I didn't realise when I wrote this, but #Michael Geary's answer is much better - avoiding Math.round() and not using length-1.
This is an old question but I will provide a new and shorter solution to get a random item from an array.
Math.random
It returns a number between 0 and 1 (1 not included).
Bitwise not ~
This operator behaves returning the oposite value that you are providing, so:
a = 5
~a // -5
It also forgets about decimals, so for instance:
a = 5.95
~a // -5
It is skipping the decimals, so somehow it behaves like Math.floor (without returning a negative value, of course).
Doubled operators
Negative logical operator !, used to coerce to a boolean type is !!null // false and we are forcing it by double negation.
If we use the same idea but for numbers, we are forcing a number to floor if we do: ~~5.999 // 5
Therefore,
TLDR;
getRandom = (arr, len = arr.length) => arr[~~(Math.random() * len)]
example:
getRandom([1,2,3,4,5]) // random item between 1 and 5

Categories

Resources