Find all permutations of a set of numbers and operators - javascript

I was given a code challenge to do that was related to recursion and was unable to complete it. My experience with these types of questions is very slim and this one just stumped me. Could any of you help me out just for my own education, as I've already failed the challenge?
The description:
Given a string of numbers and operators, print out all the different ways you can add parentheses to force the order of operations to be explicit, and the result for running the operations in that order.
Assume:
No weird inputs, everything is separated by one space.
Supported operators are +, *, -, = (for the = operator, if the values are the same return 1, otherwise return 0)
Print your results sorted numerically
Don't worry about the input expression size being too large
Your code should be written in javascript
Don't use eval or external libraries
Example:
node test.js "2 - 1 - 1"
((2-1)-1) = 0
(2-(1-1)) = 2
node test.js "2 * 3 - 4 * 5";
(2*(3-(4*5))) = -34
((2*3)-(4*5)) = -14
((2*(3-4))*5) = -10
(2*((3-4)*5)) = -10
(((2*3)-4)*5) = 10
node test.js "2 + 2 = 2"
((2+2)=2) = 0
(2+(2=2)) = 3
This is where I'm at so far. I'm far from getting the right output, but I feel like the logic is starting to get there. I've adapted this code from a similar, but different question.
var args = process.argv.slice(2)[0].split(" "),
numberOfOperators = 0;
args.forEach(function(val, index, array) {
if (isNaN(val)) {
++numberOfOperators;
}
});
args = args.join("");
var recurse = function(openParenCount, closeParenCount, input, pointer) {
if (openParenCount === 0 && closeParenCount === 0) {
console.log(input + "\n");
}
if (openParenCount > 0) {
input = input.slice(0, pointer) + "(" + input.slice(pointer, input.length);
recurse(openParenCount - 1, closeParenCount + 1, input, pointer+1);
}
if (closeParenCount > 0) {
input = input.slice(0, pointer+openParenCount+3) + ")" + input.slice(pointer+openParenCount+3, input.length+1);
recurse(openParenCount, closeParenCount - 1, input, pointer+3);
}
}
recurse(numberOfOperators, 0, args, 0);

a little hint:
var AP = [];
var input = process.argv.slice(2)[0];
var args = input.replace(/\s+/g, "").split(/([^\d\.]+)/g);
recurse(args, []).forEach(function(v){ console.log(v); });
function recurse(arr, into){
if(arr.length === 1){
into.push(arr[0]);
}else{
for(var i=0, j=arr.length-2; i<j; i+=2){
recurse(
AP.concat(
arr.slice(0, i),
"(" + arr.slice(i, i+3).join(" ") + ")",
arr.slice(i+3)
),
into
)
}
}
return into
}
This Implementation still has a few "bugs", and these by intent; I'm not going to do your "homework".
If you have more than 2 operators in your Equasion, the result will contain duplicates, 2nd It is not sorted, and since it is just splitting and concatenating strings, it can't compute any result.
But it shows you a way how you can implement the recursion.

Related

Is there a way to avoid number to string conversion & nested loops for performance?

I just took a coding test online and this one question really bothered me. My solution was correct but was rejected for being unoptimized. The question is as following:
Write a function combineTheGivenNumber taking two arguments:
numArray: number[]
num: a number
The function should check all the concatenation pairs that can result in making a number equal to num and return their count.
E.g. if numArray = [1, 212, 12, 12] & num = 1212 then we will have return value of 3 from combineTheGivenNumber
The pairs are as following:
numArray[0]+numArray[1]
numArray[2]+numArray[3]
numArray[3]+numArray[2]
The function I wrote for this purpose is as following:
function combineTheGivenNumber(numArray, num) {
//convert all numbers to strings for easy concatenation
numArray = numArray.map(e => e+'');
//also convert the `hay` to string for easy comparison
num = num+'';
let pairCounts = 0;
// itereate over the array to get pairs
numArray.forEach((e,i) => {
numArray.forEach((f,j) => {
if(i!==j && num === (e+f)) {
pairCounts++;
}
});
});
return pairCounts;
}
console.log('Test 1: ', combineTheGivenNumber([1,212,12,12],1212));
console.log('Test 2: ', combineTheGivenNumber([4,21,42,1],421));
From my experience, I know conversion of number to string is slow in JS, but I am not sure whether my approach is wrong/lack of knowledge or does the tester is ignorant of this fact. Can anyone suggest further optimization of the code snipped?
Elimination of string to number to string will be a significant speed boost but I am not sure how to check for concatenated numbers otherwise.
Elimination of string to number to string will be a significant speed boost
No, it won't.
Firstly, you're not converting strings to numbers anywhere, but more importantly the exercise asks for concatenation so working with strings is exactly what you should do. No idea why they're even passing numbers. You're doing fine already by doing the conversion only once for each number input, not every time your form a pair. And last but not least, avoiding the conversion will not be a significant improvement.
To get a significant improvement, you should use a better algorithm. #derpirscher is correct in his comment: "[It's] the nested loop checking every possible combination which hits the time limit. For instance for your example, when the outer loop points at 212 you don't need to do any checks, because regardless, whatever you concatenate to 212, it can never result in 1212".
So use
let pairCounts = 0;
numArray.forEach((e,i) => {
if (num.startsWith(e)) {
//^^^^^^^^^^^^^^^^^^^^^^
numArray.forEach((f,j) => {
if (i !== j && num === e+f) {
pairCounts++;
}
});
}
});
You might do the same with suffixes, but it becomes more complicated to rule out concatenation to oneself there.
Optimising further, you can even achieve a linear complexity solution by putting the strings in a lookup structure, then when finding a viable prefix just checking whether the missing part is an available suffix:
function combineTheGivenNumber(numArray, num) {
const strings = new Map();
for (const num of numArray) {
const str = String(num);
strings.set(str, 1 + (strings.get(str) ?? 0));
}
const whole = String(num);
let pairCounts = 0;
for (const [prefix, pCount] of strings) {
if (!whole.startsWith(prefix))
continue;
const suffix = whole.slice(prefix.length);
if (strings.has(suffix)) {
let sCount = strings.get(suffix);
if (suffix == prefix) sCount--; // no self-concatenation
pairCounts += pCount*sCount;
}
}
return pairCounts;
}
(the proper handling of duplicates is a bit difficile)
I like your approach of going to strings early. I can suggest a couple of simple optimizations.
You only need the numbers that are valid "first parts" and those that are valid "second parts"
You can use the javascript .startsWith and .endsWith to test for those conditions. All other strings can be thrown away.
The lengths of the strings must add up to the length of the desired answer
Suppose your target string is 8 digits long. If you have 2 valid 3-digit "first parts", then you only need to know how many valid 5-digit "second parts" you have. Suppose you have 9 of them. Those first parts can only combine with those second parts, and give you 2 * 9 = 18 valid pairs.
You don't actually need to keep the strings!
It struck me that if you know you have 2 valid 3-digit "first parts", you don't need to keep those actual strings. Knowing that they are valid 2-digit first parts is all you need to know.
So let's build an array containing:
How many valid 1-digit first parts do we have?,
How many valid 2-digit first parts do we have?,
How many valid 3-digit first parts do we have?,
etc.
And similarly an array containing the number of valid 1-digit second parts, etc.
X first parts and Y second parts can be combined in X * Y ways
Except if the parts are the same length, in which case we are reusing the same list, and so it is just X * (Y-1).
So not only do we not need to keep the strings, but we only need to do the multiplication of the appropriate elements of the arrays.
5 1-char first parts & 7 3-char second parts = 5 * 7 = 35 pairs
6 2-char first part & 4 2-char second parts = 6 * (4-1) = 18 pairs
etc
So this becomes extremely easy. One pass over the strings, tallying the "first part" and "second part" matches of each length. This can be done with an if and a ++ of the relevant array element.
Then one pass over the lengths, which will be very quick as the array of lengths will be very much shorter than the array of actual strings.
function combineTheGivenNumber(numArray, num) {
const sElements = numArray.map(e => "" + e);
const sTarget = "" + num;
const targetLength = sTarget.length
const startsByLen = (new Array(targetLength)).fill(0);
const endsByLen = (new Array(targetLength)).fill(0);
sElements.forEach(sElement => {
if (sTarget.startsWith(sElement)) {
startsByLen[sElement.length]++
}
if (sTarget.endsWith(sElement)) {
endsByLen[sElement.length]++
}
})
// We can now throw away the strings. We have two separate arrays:
// startsByLen[1] is the count of strings (without attempting to remove duplicates) which are the first character of the required answer
// startsByLen[2] similarly the count of strings which are the first 2 characters of the required answer
// etc.
// and endsByLen[1] is the count of strings which are the last character ...
// and endsByLen[2] is the count of strings which are the last 2 characters, etc.
let pairCounts = 0;
for (let firstElementLength = 1; firstElementLength < targetLength; firstElementLength++) {
const secondElementLength = targetLength - firstElementLength;
if (firstElementLength === secondElementLength) {
pairCounts += startsByLen[firstElementLength] * (endsByLen[secondElementLength] - 1)
} else {
pairCounts += startsByLen[firstElementLength] * endsByLen[secondElementLength]
}
}
return pairCounts;
}
console.log('Test 1: ', combineTheGivenNumber([1, 212, 12, 12], 1212));
console.log('Test 2: ', combineTheGivenNumber([4, 21, 42, 1], 421));
Depending on a setup, the integer slicing can be marginally faster
Although in the end it falls short
Also, when tested on higher N values, the previous answer exploded in jsfiddle. Possibly a memory error.
As far as I have tested with both random and hand-crafted values, my solution holds. It is based on an observation, that if X, Y concantenated == Z, then following must be true:
Z - Y == X * 10^(floor(log10(Y)) + 1)
an example of this:
1212 - 12 = 1200
12 * 10^(floor((log10(12)) + 1) = 12 * 10^(1+1) = 12 * 100 = 1200
Now in theory, this should be faster then manipulating strings. And in many other languages it most likely would be. However in Javascript as I just learned, the situation is a bit more complicated. Javascript does some weird things with casting that I haven't figured out yet. In short - when I tried storing the numbers(and their counts) in a map, the code got significantly slower making any possible gains from this logarithm shenanigans evaporate. Furthermore, storing them in a custom-crafted data structure isn't guaranteed to be faster since you have to build it etc. Also it would be quite a lot of work.
As it stands this log comparison is ~ 8 times faster in a case without(or with just a few) matches since the quadratic factor is yet to kick in. As long as the possible postfix count isn't too high, it will outperform the linear solution. Unfortunately it is still quadratic in nature with the breaking point depending on a total number of strings as well as their length.
So if you are searching for a needle in a haystack - for example you are looking for a few pairs in a huge heap of numbers, this can help. In the other case of searching for many matches, this won't help. Similarly, if the input array was sorted, you could use binary search to push the breaking point further up.
In the end, unless you manage to figure out how to store ints in a map(or some custom implementation of it) in a way that doesn't completely kill the performance, the linear solution of the previous answer will be faster. It can still be useful even with the performance hit if your computation is going to be memory heavy. Storing numbers takes less space then storing strings.
var log10 = Math.log(10)
function log10floored(num) {
return Math.floor(Math.log(num) / log10)
}
function combineTheGivenNumber(numArray, num) {
count = 0
for (var i=0; i!=numArray.length; i++) {
let portion = num - numArray[i]
let removedPart = Math.pow(10, log10floored(numArray[i]))
if (portion % (removedPart * 10) == 0) {
for (var j=0; j!=numArray.length; j++) {
if (j != i && portion / (removedPart * 10) == numArray[j] ) {
count += 1
}
}
}
}
return count
}
//The previous solution, that I used for timing, comparison and check purposes
function combineTheGivenNumber2(numArray, num) {
const strings = new Map();
for (const num of numArray) {
const str = String(num);
strings.set(str, 1 + (strings.get(str) ?? 0));
}
const whole = String(num);
let pairCounts = 0;
for (const [prefix, pCount] of strings) {
if (!whole.startsWith(prefix))
continue;
const suffix = whole.slice(prefix.length);
if (strings.has(suffix)) {
let sCount = strings.get(suffix);
if (suffix == prefix) sCount--; // no self-concatenation
pairCounts += pCount*sCount;
}
}
return pairCounts;
}
var myArray = []
for (let i =0; i!= 10000000; i++) {
myArray.push(Math.floor(Math.random() * 1000000))
}
var a = new Date()
t1 = a.getTime()
console.log('Test 1: ', combineTheGivenNumber(myArray,15285656));
var b = new Date()
t2 = b.getTime()
console.log('Test 2: ', combineTheGivenNumber2(myArray,15285656));
var c = new Date()
t3 = c.getTime()
console.log('Test1 time: ', t2 - t1)
console.log('test2 time: ', t3 - t2)
Small update
As long as you are willing to take a performance hit with the setup and settle for the ~2 times performance, using a simple "hashing" table can help.(Hashing tables are nice and tidy, this is a simple modulo lookup table. The principle is similar though.)
Technically this isn't linear, practicaly it is enough for the most cases - unless you are extremely unlucky and all your numbers fall in the same bucket.
function combineTheGivenNumber(numArray, num) {
count = 0
let size = 1000000
numTable = new Array(size)
for (var i=0; i!=numArray.length; i++) {
let idx = numArray[i] % size
if (numTable[idx] == undefined) {
numTable[idx] = [numArray[i]]
} else {
numTable[idx].push(numArray[i])
}
}
for (var i=0; i!=numArray.length; i++) {
let portion = num - numArray[i]
let removedPart = Math.pow(10, log10floored(numArray[i]))
if (portion % (removedPart * 10) == 0) {
if (numTable[portion / (removedPart * 10) % size] != undefined) {
let a = numTable[portion / (removedPart * 10) % size]
for (var j=0; j!=a.length; j++) {
if (j != i && portion / (removedPart * 10) == a[j] ) {
count += 1
}
}
}
}
}
return count
}
Here's a simplified, and partially optimised approach with 2 loops:
// let's optimise 'combineTheGivenNumber', where
// a=array of numbers AND n=number to match
const ctgn = (a, n) => {
// convert our given number to a string using `toString` for clarity
// this isn't entirely necessary but means we can use strict equality later
const ns = n.toString();
// reduce is an efficient mechanism to return a value based on an array, giving us
// _=[accumulator], na=[array number] and i=[index]
return a.reduce((_, na, i) => {
// convert our 'array number' to an 'array number string' for later concatenation
const nas = na.toString();
// iterate back over our array of numbers ... we're using an optimised/reverse loop
for (let ii = a.length - 1; ii >= 0; ii--) {
// skip the current array number
if (i === ii) continue;
// string + number === string, which lets us strictly compare our 'number to match'
// if there's a match we increment the accumulator
if (a[ii] + nas === ns) ++_;
}
// we're done
return _;
}, 0);
}

What is the Time Complexity for this solution of trimming a message of N length at M length?

I'm trying to get the correct Time Complexity of my solution for the following question:
Input: a string contains spaces and words, and an integer M
Output: trim the string so the trimmed string length <= m and no cut-off word.
Sample:
S = "This is JavaScript", M = 10.
Expect: "This is" // because "This is Ja" is invalid, "JavaScript" is cut.
S = "JavaScript", M = 5
Expect: "" // empty because the output "JavaS" is invalid.
S = "JavaScript", M = 10
Expect: "JavaScript"
Here's my code:
function trimThis(S, M) {
if (M > S.length) return S;
let output = S.slice(); // Making a copy of the input is optional
while (output.length > M) {
const lastSpaceIdx = output.lastIndexOf(' ');
if (lastSpaceIdx !== -1) {
const endIdx = Math.min(lastSpaceIdx, output.length);
output = output.slice(0, endIdx);
}
else return '';
}
// In JS we can also use trimEnd() to remove trailing spaces:
while (output.length > 0 && output[output.length - 1] === ' ') {
output = output.slice(0, -1);
}
return output;
}
I'm thinking the total Time Complexity is O(N) where n = len(S) but I'm not sure because of the while loop.
The while loop will run O(N - M) times. In each iteration, the code does O(N) operation to find the last space index and the slice() operation takes O(N) so it looks like O(N^2). However, after each iteration, the string gets smaller and smaller.
The time to make the copy string O(N) won't affect the overall Big O.
The time complexity is O(n²), because you perform a slice repeatedly, which creates a new string each time. Although the implementation of slice is fast, it is not constant in time, but proportional to its length.
You should just work with the index, and just perform the "costly" slice at the very end. Note also that the initial slice is not needed as any way strings are immutable. It is not possible to modify S, even if you wanted to.
Here is your code adapted to only perform a slice at the end. NB: I wouldn't use initial capitals for variable names, as commonly that is reserved for names of constructors.
function trimThis(s, m) {
let endIdx = s.length;
while (endIdx > m) {
lastSpaceIdx = s.lastIndexOf(' ', endIdx - 1);
if (lastSpaceIdx !== -1) {
endIdx = lastSpaceIdx;
} else return '';
}
while (endIdx > 0 && s[endIdx - 1] === ' ') {
endIdx--;
}
return s.slice(0, endIdx);
}
let s = "We are living in interesting times";
let res = trimThis(s, 20);
console.log(res);
Now it is O(n).

Need help on Highest and Lowest (codewars)

Hello I'm stuck on an edge case in a coding challenge: would be great if someone could help;
In this little assignment you are given a string of space separated numbers, and have to return the highest and lowest number.
Example:
highAndLow("1 2 3 4 5"); // return "5 1"
highAndLow("1 2 -3 4 5"); // return "5 -3"
highAndLow("1 9 3 4 -5"); // return "9 -5"
Notes:
All numbers are valid Int32, no need to validate them.
There will always be at least one number in the input string.
Output string must be two numbers separated by a single space, and highest number is first.
Here is my code in Javascript:
function highAndLow(numbers){
numbers2=numbers.split(' ');
var highest =parseInt(numbers2[0]);
var lowest =parseInt(numbers2[0]);
if (numbers2.length==1) {
return numbers;
}
else {
for (i=0;i<numbers2.length;i++) {
if (parseInt(numbers2[i])>highest) {
highest = parseInt(numbers2[i]);
}
else if (parseInt(numbers2[i])<lowest) {
lowest = parseInt(numbers2[i]);
}
}
}
return(highest + " " + lowest);
}
I can pass 17 tests but am stuck on an Expected '42 42' because I am returning '42' which is puzzling to me. Any help appreciated :]
I think you should just add <= and >= instead of > and <so the both conditions are satisfied
You can also do it by sorting the array and then choosing the first and last element from the sorted array.
function highestAndLowest(nums) {
let numbers = nums.split(' ');
let sorted = numbers.sort(function (a, b) {
return Number(a) - Number(b);
});
return sorted[0] + " " + sorted[sorted.length - 1];
}
https://jsbin.com/farapep/edit?js,console
This can be faster depending on the browsers sort implementation, the size of the array, and the initial order of the array.
I think it will work well just like this
function highAndLow(numbers){
numbers = numbers.split(" ");
return Math.max(...numbers) +" "+ Math.min(...numbers);
}
if (numbers2.length==1) {
return numbers;
}
That means if just "42" passed, you return "42". Thats not required here. Just remove that and it should work. How i would write it:
function getMaxMin(numbers){
numbers = numbers.split(" ");
return Math.max(...numbers) +" "+ Math.min(...numbers);
}
or your code a bit beautified:
function getMaxMin(numbers){
var max,min;
numbers = numbers.split(" ");
for( var num of numbers ){
if( !max || num > max ) max = num;
if( !min || num < min ) min = num;
}
return max+" "+min;
}
Kotlin
fun highAndLow(numbers: String): String {
val s = numbers.split(" ").sorted()
return "${s.last()} ${s.first()}"
}
or in one line
fun highAndLow(numbers: String): String = numbers.split(" ").sorted().run { "${first()} ${last()}" }
Let us consider the given example:
highAndLow("1 2 3 4 5"); // return "5 1"
On calling function highAndLow with arguments ("1 2 3 4 5"), output should be "5 1".
So my function takes the argument. Each number is picked upon on the basis of space between them(used split method). I have used ParseInt to specify the datatype, because var can be anything(string/integer). The algorithm used is very basic one which considers the first number as the maximum and compares its with the rest of the arguments. Value of max is updated if it finds a number greater than itself. Same algorithm is used for min value also. The return statement is designed to get the value in specific way as mentioned in the example.
function highAndLow(numbers){
num=numbers.split(' ');
var max = parseInt(num[0]);
var min = parseInt(num[0]);
for (var i = 0; i <= num.length; i++) {
if(parseInt(num[i]) > max){
max = parseInt(num[i]);
}
}
for (var i = 0; i <= num.length; i++) {
if(parseInt(num[i]) < min){
min = parseInt(num[i]);
}
}
return (max + " " + min);
}
You can also use inbuilt methods min and max. I wanted to solve this question without using them.

Adding numbers together

I want to loop over an array whilst addding the numbers together.
Whilst looping over the array, I would like to add the current number to the next.
My array looks like
[0,1,0,4,1]
I would like to do the following;
[0,1,0,4,1] - 0+1= 1, 1+0= 1, 0+4=4, 4+1=5
which would then give me [1,1,4,5] to do the following; 1+1 = 2, 1+4=5, 4+5=9
and so on until I get 85.
Could anyone advise on the best way to go about this
This transform follows the specified method of summation, but I also get an end result of 21, so please specify how you get to 85.
var ary = [0,1,0,4,1],
transform = function (ary) {
var length = ary.length;
return ary.reduce(function (acc, val, index, ary) {
if (index + 1 !== length) acc.push(ary[index] + ary[index + 1]);
return acc;
}, []);
};
while (ary.length !== 1) ary = transform(ary);
If you do in fact want the answer to be 21 (as it seems like it should be), what you are really trying to do is closely related to the Binomial Theorem.
I am not familiar with javascript, so I will write an example in c-style pseudocode:
var array = [0,1,0,4,1]
int result = 0;
for (int i = 0; i < array.length; i++)
{
int result += array[i] * nChooseK(array.length - 1, i);
}
This will put the following numbers into result for each respective iteration:
0 += 0 * 1 --> 0
0 += 1 * 4 --> 4
4 += 0 * 6 --> 4
4 += 4 * 4 --> 20
20 += 1 * 1 --> 21
This avoids all the confusing array operations that arise when trying to iterate through creating shorter-and-shorter arrays; it will also be faster if you have a good nChooseK() implementation.
Now, finding an efficient algorithm for a nChooseK() function is a different matter, but it is a relatively common task so it shouldn't be too difficult (Googling "n choose k algorithm" should work just fine). Some languages even have combinatoric functions in standard math libraries.
The result I get is 21 not 85. This code can be optimised to only use single array. Anyway it gets the job done.
var input = [0, 1, 0, 4, 1];
function calc(input) {
if (input.length === 1) {
return input;
}
var result = [];
for (var i = 0; i < input.length - 1; i++) {
result[i] = input[i] + input[i + 1];
}
return calc(result);
}
alert(calc(input));
This is an O(n^2) algorithm.

How can I find the length of a number?

I'm looking to get the length of a number in JavaScript or jQuery?
I've tried value.length without any success, do I need to convert this to a string first?
var x = 1234567;
x.toString().length;
This process will also work forFloat Number and for Exponential number also.
Ok, so many answers, but this is a pure math one, just for the fun or for remembering that Math is Important:
var len = Math.ceil(Math.log(num + 1) / Math.LN10);
This actually gives the "length" of the number even if it's in exponential form. num is supposed to be a non negative integer here: if it's negative, take its absolute value and adjust the sign afterwards.
Update for ES2015
Now that Math.log10 is a thing, you can simply write
const len = Math.ceil(Math.log10(num + 1));
Could also use a template string:
const num = 123456
`${num}`.length // 6
You have to make the number to string in order to take length
var num = 123;
alert((num + "").length);
or
alert(num.toString().length);
I've been using this functionality in node.js, this is my fastest implementation so far:
var nLength = function(n) {
return (Math.log(Math.abs(n)+1) * 0.43429448190325176 | 0) + 1; 
}
It should handle positive and negative integers (also in exponential form) and should return the length of integer part in floats.
The following reference should provide some insight into the method:
Weisstein, Eric W. "Number Length." From MathWorld--A Wolfram Web Resource.
I believe that some bitwise operation can replace the Math.abs, but jsperf shows that Math.abs works just fine in the majority of js engines.
Update: As noted in the comments, this solution has some issues :(
Update2 (workaround) : I believe that at some point precision issues kick in and the Math.log(...)*0.434... just behaves unexpectedly. However, if Internet Explorer or Mobile devices are not your cup of tea, you can replace this operation with the Math.log10 function. In Node.js I wrote a quick basic test with the function nLength = (n) => 1 + Math.log10(Math.abs(n) + 1) | 0; and with Math.log10 it worked as expected. Please note that Math.log10 is not universally supported.
There are three way to do it.
var num = 123;
alert(num.toString().length);
better performance one (best performance in ie11)
var num = 123;
alert((num + '').length);
Math (best performance in Chrome, firefox but slowest in ie11)
var num = 123
alert(Math.floor( Math.log(num) / Math.LN10 ) + 1)
there is a jspref here
http://jsperf.com/fastest-way-to-get-the-first-in-a-number/2
You should go for the simplest one (stringLength), readability always beats speed. But if you care about speed here are some below.
Three different methods all with varying speed.
// 34ms
let weissteinLength = function(n) {
return (Math.log(Math.abs(n)+1) * 0.43429448190325176 | 0) + 1;
}
// 350ms
let stringLength = function(n) {
return n.toString().length;
}
// 58ms
let mathLength = function(n) {
return Math.ceil(Math.log(n + 1) / Math.LN10);
}
// Simple tests below if you care about performance.
let iterations = 1000000;
let maxSize = 10000;
// ------ Weisstein length.
console.log("Starting weissteinLength length.");
let startTime = Date.now();
for (let index = 0; index < iterations; index++) {
weissteinLength(Math.random() * maxSize);
}
console.log("Ended weissteinLength length. Took : " + (Date.now() - startTime ) + "ms");
// ------- String length slowest.
console.log("Starting string length.");
startTime = Date.now();
for (let index = 0; index < iterations; index++) {
stringLength(Math.random() * maxSize);
}
console.log("Ended string length. Took : " + (Date.now() - startTime ) + "ms");
// ------- Math length.
console.log("Starting math length.");
startTime = Date.now();
for (let index = 0; index < iterations; index++) {
mathLength(Math.random() * maxSize);
}
First convert it to a string:
var mynumber = 123;
alert((""+mynumber).length);
Adding an empty string to it will implicitly cause mynumber to turn into a string.
Well without converting the integer to a string you could make a funky loop:
var number = 20000;
var length = 0;
for(i = number; i > 1; ++i){
++length;
i = Math.floor(i/10);
}
alert(length);​
Demo: http://jsfiddle.net/maniator/G8tQE/
I got asked a similar question in a test.
Find a number's length without converting to string
const numbers = [1, 10, 100, 12, 123, -1, -10, -100, -12, -123, 0, -0]
const numberLength = number => {
let length = 0
let n = Math.abs(number)
do {
n /= 10
length++
} while (n >= 1)
return length
}
console.log(numbers.map(numberLength)) // [ 1, 2, 3, 2, 3, 1, 2, 3, 2, 3, 1, 1 ]
Negative numbers were added to complicate it a little more, hence the Math.abs().
I'm perplex about converting into a string the given number because such an algorithm won't be robust and will be prone to errors: it will show all its limitations especially in case it has to evaluate very long numbers. In fact before converting the long number into a string it will "collapse" into its exponential notation equivalent (example: 1.2345e4). This notation will be converted into a string and this resulting string will be evaluated for returning its length. All of this will give a wrong result. So I suggest not to use that approach.
Have a look at the following code and run the code snippet to compare the different behaviors:
let num = 116234567891011121415113441236542134465236441625344625344625623456723423523429798771121411511034412365421344652364416253446253446254461253446221314623879235441623683749283441136232514654296853446323214617456789101112141511344122354416236837492834411362325146542968534463232146172368374928344113623251465429685;
let lenFromMath;
let lenFromString;
// The suggested way:
lenFromMath = Math.ceil(Math.log10(num + 1)); // this works in fact returns 309
// The discouraged way:
lenFromString = String(num).split("").length; // this doesn't work in fact returns 23
/*It is also possible to modify the prototype of the primitive "Number" (but some programmer might suggest this is not a good practice). But this is will also work:*/
Number.prototype.lenght = () => {return Math.ceil(Math.log10(num + 1));}
lenFromPrototype = num.lenght();
console.log({lenFromMath, lenFromPrototype, lenFromString});
A way for integers or for length of the integer part without banal converting to string:
var num = 9999999999; // your number
if (num < 0) num = -num; // this string for negative numbers
var length = 1;
while (num >= 10) {
num /= 10;
length++;
}
alert(length);
I would like to correct the #Neal answer which was pretty good for integers, but the number 1 would return a length of 0 in the previous case.
function Longueur(numberlen)
{
var length = 0, i; //define `i` with `var` as not to clutter the global scope
numberlen = parseInt(numberlen);
for(i = numberlen; i >= 1; i)
{
++length;
i = Math.floor(i/10);
}
return length;
}
To get the number of relevant digits (if the leading decimal part is 0 then the whole part has a length of 0) of any number separated by whole part and decimal part I use:
function getNumberLength(x) {
let numberText = x.toString();
let exp = 0;
if (numberText.includes('e')) {
const [coefficient, base] = numberText.split('e');
exp = parseInt(base, 10);
numberText = coefficient;
}
const [whole, decimal] = numberText.split('.');
const wholeLength = whole === '0' ? 0 : whole.length;
const decimalLength = decimal ? decimal.length : 0;
return {
whole: wholeLength > -exp ? wholeLength + exp : 0,
decimal: decimalLength > exp ? decimalLength - exp : 0,
};
}
var x = 1234567;
String(x).length;
It is shorter than with .toString() (which in the accepted answer).
Try this:
$("#element").text().length;
Example of it in use
Yes you need to convert to string in order to find the length.For example
var x=100;// type of x is number
var x=100+"";// now the type of x is string
document.write(x.length);//which would output 3.

Categories

Resources