I want to convert a number into a string representation with a format similar to Stack Overflow reputation display.
e.g.
999 == '999'
1000 == '1,000'
9999 == '9,999'
10000 == '10k'
10100 == '10.1k'
Another approach that produces exactly the desired output:
function getRepString (rep) {
rep = rep+''; // coerce to string
if (rep < 1000) {
return rep; // return the same number
}
if (rep < 10000) { // place a comma between
return rep.charAt(0) + ',' + rep.substring(1);
}
// divide and format
return (rep/1000).toFixed(rep % 1000 != 0)+'k';
}
Check the output results here.
UPDATE:
CMS got the check and provides a superior answer. Send any more votes his way.
// formats a number similar to the way stack exchange sites
// format reputation. e.g.
// for numbers< 10000 the output is '9,999'
// for numbers > 10000 the output is '10k' with one decimal place when needed
function getRepString(rep)
{
var repString;
if (rep < 1000)
{
repString = rep;
}
else if (rep < 10000)
{
// removed my rube goldberg contraption and lifted
// CMS version of this segment
repString = rep.charAt(0) + ',' + rep.substring(1);
}
else
{
repString = (Math.round((rep / 1000) * 10) / 10) + "k"
}
return repString.toString();
}
Output:
getRepString(999) == '999'
getRepString(1000) == '1,000'
getRepString(9999) == '9,999'
getRepString(10000) == '10k'
getRepString(10100) == '10.1k'
Here is a function in PHP which is part of iZend - http://www.izend.org/en/manual/library/countformat:
function count_format($n, $point='.', $sep=',') {
if ($n < 0) {
return 0;
}
if ($n < 10000) {
return number_format($n, 0, $point, $sep);
}
$d = $n < 1000000 ? 1000 : 1000000;
$f = round($n / $d, 1);
return number_format($f, $f - intval($f) ? 1 : 0, $point, $sep) . ($d == 1000 ? 'k' : 'M');
}
Here is CMS's version in PHP (in case someone needed it, like I did):
function getRepString($rep) {
$rep = intval($rep);
if ($rep < 1000) {
return (string)$rep;
}
if ($rep < 10000) {
return number_format($rep);
}
return number_format(($rep / 1000), ($rep % 1000 != 0)) . 'k';
}
// TEST
var_dump(getRepString(999));
var_dump(getRepString(1000));
var_dump(getRepString(9999));
var_dump(getRepString(10000));
var_dump(getRepString(10100));
Output:
string(3) "999"
string(5) "1,000"
string(5) "9,999"
string(3) "10k"
string(5) "10.1k"
Handlebars.registerHelper("classNameHere",function(rep) {
var repString;
if (rep < 1000)
{
repString = rep;
}
else if (rep < 10000)
{
rep = String(rep);
r = rep.charAt(0);
s = rep.substring(1);
repString = r + ',' + s;
}
else
{
repDecimal = Math.round(rep / 100) / 10;
repString = repDecimal + "k";
}
return repString.toString();
});
divide by 1000 then if result is greater than 1 round the number and concantenate a "k" on the end.
If the result is less than 1 just output the actual result!
// Shortens a number and attaches K, M, B, etc. accordingly
function number_shorten($number, $precision = 3, $divisors = null) {
// Setup default $divisors if not provided
if (!isset($divisors)) {
$divisors = array(
pow(1000, 0) => '', // 1000^0 == 1
pow(1000, 1) => 'K', // Thousand
pow(1000, 2) => 'M', // Million
pow(1000, 3) => 'B', // Billion
pow(1000, 4) => 'T', // Trillion
pow(1000, 5) => 'Qa', // Quadrillion
pow(1000, 6) => 'Qi', // Quintillion
);
}
// Loop through each $divisor and find the
// lowest amount that matches
foreach ($divisors as $divisor => $shorthand) {
if (abs($number) < ($divisor * 1000)) {
// We found a match!
break;
}
}
// We found our match, or there were no matches.
// Either way, use the last defined value for $divisor.
return number_format($number / $divisor, $precision) . $shorthand;
}
This worked for me. I hope, this will help you. Thanks for asking this question.
I created an npm (and bower) module to do this:
npm install --save approximate-number
Usage:
var approx = require('approximate-number');
approx(123456); // "123k"
Related
How do you check whether or not an integer contains a digit?
For example:
var n = 12;
var m = 34;
n contains 1 // true
m contains 1 // false
What's the fastest (performance wise) way to do this without turning the integer into a string?
Refer to the following code (if the comments aren't good enough feel free to ask):
function contains(number, digit) {
if (number < 0) { // make sure negatives are dealt with properly, alternatively replace this if statement with number = Math.abs(number)
number *= -1;
}
if (number == digit) { // this is to deal with the number=0, digit=0 edge case
return true;
}
while (number != 0) { // stop once all digits are cut off
if (number % 10 == digit) { // check if the last digit matches
return true;
}
number = Math.floor(number / 10); // cut off the last digit
}
return false;
}
Here's a simple recursive form -
const contains = (q, p) =>
p < 10
? p === q
: p % 10 === q || contains(q, p / 10 >>> 0)
console.log(contains(1, 12)) // true
console.log(contains(1, 34)) // false
console.log(contains(9, 14293)) // true
console.log(contains(9, 1212560283)) // false
if (n.toString().includes("1")) {
/// Do something
}
try this:
let n = 1234;
let flag = false;
while (n > 0){
r = n % 10;
if(r == 1){
flag = true;
break;
}
n = (n - (n % 10)) / 10;
}
console.log("n contains 1 = "+flag);
So I have this question, which I don't quite understand.
I would like to understand the approach of the problem
Imagine the following scenario:
You are the HR manager of a company with 1000 employees numbered for 1 to 1000. Your boss told you to give a big Christmas bonus to employees, but didn’t tell you their names. Instead they gave you two indications:
1) the sum of the proper divisors (including 1 but not itself) of the employee number is greater than the employee number itself
2) no subset of those divisors sums to the employee number itself.
How many employees are eligible for the bonus and what are their number?
For example:
- Number 12: the proper divisors are 1, 2, 3, 4 and 6. The sum is 1+2+3+4+6 = 16 which is greater than 12 and matches the first condition. However, the subset 2+4+6=12 which violates the second condition.
My conclusion is:
I have to get those numbers from 1 to 1000, where the sum of the number's divisors are greater than the number itself (including 1 but not itself), but none of the divisors' subsets can be added to be equal with the number itself?
My steps would be:
Put the divisors of the numbers from 1 to 1000 into an array
Get those numbers, when the divisors' sum (including 1 but not itself) are greater than the number itself and resize the array to only those numbers.
I have to check every subset of the remaining number's divisors and remove those when a subset of the divisors can be equal with the number itself.
Could you help me if it's a good approach or do any of you know a more efficient/better way?
Any help would be appreciated!
NOTE: I don't want you to solve it, I want to understand it!
I have made this so far, which covers the first two steps that I intended to do. Last step is beyond my knowledge, but I have the solution for that also.
This is my code:
<script type="text/javascript">
function getDivisors(n){
var divisors=new Array();
for(var x=1;x<n;x++){
if(n%x==0) divisors.push(x);
}
return divisors;
}
function getNumbers(n){
var numbers=new Array(),
sum=0;
for(var x=1;x<=n;x++){
sum=getDivisors(x).reduce((a, b) => a + b, 0);
if(sum>x) numbers.push(x);
// console.log("Number: "+x+" sum:"+sum);
}
return numbers;
}
var remainingNumbers = getNumbers(1000);
console.log(remainingNumbers);
</script>
This is the answer for the question:
var out = document.getElementById('outLine');
out.innerHTML += "X\t»\tSUM\tSUM-X\tLIST\r\n";
function isSubsetSum(set, n, sum) {
if (sum == 0) { return true; }
if (n == 0 && sum != 0) { return false; }
if (set[n - 1] > sum) { return isSubsetSum(set, n - 1, sum); }
return isSubsetSum(set, n - 1, sum) ||
isSubsetSum(set, n - 1, sum - set[n - 1]);
}
function v1chNum(x) {
var r = [1];
var n = 0;
var m = x/2;
for(var i = 2; i <= m; i++ ) {
if(x%i==0) {
if(r.indexOf(i)==-1) {
r.push(i);
n += i;
}
m = x/i;
if(r.indexOf(m)==-1) {
r.push(m);
n += m;
}
}
}
if( n > x ) {
r.sort(function(a, b) {return b - a;});
if(!isSubsetSum(r,r.length,x)) {
out.innerHTML += x+"\t»\t"+n+"\t"+(n-x)+"\t"+r+"\r\n";
} else { return false; }
} else { return false; }
}
for(var x = 1; x<1000; x++) {
v1chNum(x);
}
<pre id="outLine"></pre>
PHP approach:
$empList = [];
for ($emp = 1; $emp <= 100; $emp++) {
$multiples = [];
$subset = [];
$cond1 = false;
$cond2 = false;
// Get multiples.
for ($i = 1; $i < $emp; $i++) {
if ($emp % $i == 0) $multiples[]= $i;
}
// Condition 1
if (array_sum($multiples) > $emp) $cond1 = true;
foreach ($multiples as $num) {
if ($num % 2 == 0) $subset[]= $num;
}
// Condition 2
if (array_sum($subset) > $emp) $cond2 = true;
if ($cond1 && $cond2) $empList[] = $emp;
}
echo "<pre>";
var_dump($empList);
echo "</pre>";
Output:
Array
(
[0] => 24
[1] => 36
[2] => 40
[3] => 48
[4] => 60
[5] => 72
[6] => 80
[7] => 84
[8] => 96
)
My code in python:
def getDivisors(n):
div = []
for i in range(1, n-1):
if(n%i == 0):
div.append(i)
return div
def sumDivisors(arr):
div_sum = 0
for i in arr:
div_sum += i
return div_sum
def subLists(arr):
lists = [[]]
for i in range(len(arr)):
orig = lists[:]
new = arr[i]
for j in range(len(lists)):
lists[j] = lists[j] + [new]
lists = orig + lists
return lists
def sumSublists(lists, n):
for i in range(len(lists)):
sum_list = sum(lists[i])
if (sum_list == n):
return False
return True
for num in range(100):
arr = getDivisors(int(num))
lists = subLists(arr)
if ((sumDivisors(arr) > int(num))and(sumSublists(lists, int(num)))):
print(str(num) + '/n')
I need to convert a Google Spreadsheet column index into its corresponding letter value, for example, given a spreadsheet:
I need to do this (this function obviously does not exist, it's an example):
getColumnLetterByIndex(4); // this should return "D"
getColumnLetterByIndex(1); // this should return "A"
getColumnLetterByIndex(6); // this should return "F"
Now, I don't recall exactly if the index starts from 0 or from 1, anyway the concept should be clear.
I didn't find anything about this on gas documentation.. am I blind? Any idea?
Thank you
I wrote these a while back for various purposes (will return the double-letter column names for column numbers > 26):
function columnToLetter(column)
{
var temp, letter = '';
while (column > 0)
{
temp = (column - 1) % 26;
letter = String.fromCharCode(temp + 65) + letter;
column = (column - temp - 1) / 26;
}
return letter;
}
function letterToColumn(letter)
{
var column = 0, length = letter.length;
for (var i = 0; i < length; i++)
{
column += (letter.charCodeAt(i) - 64) * Math.pow(26, length - i - 1);
}
return column;
}
This works good
=REGEXEXTRACT(ADDRESS(ROW(); COLUMN()); "[A-Z]+")
even for columns beyond Z.
Simply replace COLUMN() with your column number. The value of ROW() doesn't matter.
No need to reinvent the wheel here, use the GAS range instead:
var column_index = 1; // your column to resolve
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range = sheet.getRange(1, column_index, 1, 1);
Logger.log(range.getA1Notation().match(/([A-Z]+)/)[0]); // Logs "A"
=SUBSTITUTE(ADDRESS(1,COLUMN(),4), "1", "")
This takes your cell, gets it's address as e.g. C1, and removes the "1".
How it works
COLUMN() gives the number of the column of the cell.
ADDRESS(1, ..., <format>) gives an address of a cell, in format speficied by <format> parameter. 4 means the address you know - e.g. C1.
The row doesn't matter here, so we use 1.
See ADDRESS docs
Finally, SUBSTITUTE(..., "1", "") replaces the 1 in the address C1, so you're left with the column letter.
This works on ranges A-Z
formula =char(64+column())
js String.fromCharCode(64+colno)
an google spreadsheet appscript code, based on #Gardener would be:
function columnName(index) {
var cname = String.fromCharCode(65 + ((index - 1) % 26));
if (index > 26)
cname = String.fromCharCode(64 + (index - 1) / 26) + cname;
return cname;
}
In javascript:
X = (n) => (a=Math.floor(n/26)) >= 0 ? X(a-1) + String.fromCharCode(65+(n%26)) : '';
console.assert (X(0) == 'A')
console.assert (X(25) == 'Z')
console.assert (X(26) == 'AA')
console.assert (X(51) == 'AZ')
console.assert (X(52) == 'BA')
Adding to #SauloAlessandre's answer, this will work for columns up from A-ZZ.
=if(column() >26,char(64+(column()-1)/26),) & char(65 + mod(column()-1,26))
I like the answers by #wronex and #Ondra Žižka. However, I really like the simplicity of #SauloAlessandre's answer.
So, I just added the obvious code to allow #SauloAlessandre's answer to work for wider spreadsheets.
As #Dave mentioned in his comment, it does help to have a programming background, particularly one in C where we added the hex value of 'A' to a number to get the nth letter of the alphabet as a standard pattern.
Answer updated to catch the error pointed out by #Sangbok Lee. Thank you!
I was looking for a solution in PHP. Maybe this will help someone.
<?php
$numberToLetter = function(int $number)
{
if ($number <= 0) return null;
$temp; $letter = '';
while ($number > 0) {
$temp = ($number - 1) % 26;
$letter = chr($temp + 65) . $letter;
$number = ($number - $temp - 1) / 26;
}
return $letter;
};
$letterToNumber = function(string $letters) {
$letters = strtoupper($letters);
$letters = preg_replace("/[^A-Z]/", '', $letters);
$column = 0;
$length = strlen($letters);
for ($i = 0; $i < $length; $i++) {
$column += (ord($letters[$i]) - 64) * pow(26, $length - $i - 1);
}
return $column;
};
var_dump($numberToLetter(-1));
var_dump($numberToLetter(26));
var_dump($numberToLetter(27));
var_dump($numberToLetter(30));
var_dump($letterToNumber('-1A!'));
var_dump($letterToNumber('A'));
var_dump($letterToNumber('B'));
var_dump($letterToNumber('Y'));
var_dump($letterToNumber('Z'));
var_dump($letterToNumber('AA'));
var_dump($letterToNumber('AB'));
Output:
NULL
string(1) "Z"
string(2) "AA"
string(2) "AD"
int(1)
int(1)
int(2)
int(25)
int(26)
int(27)
int(28)
Simple way through Google Sheet functions, A to Z.
=column(B2) : value is 2
=address(1, column(B2)) : value is $B$1
=mid(address(1, column(B2)),2,1) : value is B
It's a complicated way through Google Sheet functions, but it's also more than AA.
=mid(address(1, column(AB3)),2,len(address(1, column(AB3)))-3) : value is AB
I also was looking for a Python version here is mine which was tested on Python 3.6
def columnToLetter(column):
character = chr(ord('A') + column % 26)
remainder = column // 26
if column >= 26:
return columnToLetter(remainder-1) + character
else:
return character
A comment on my answer says you wanted a script function for it. All right, here we go:
function excelize(colNum) {
var order = 1, sub = 0, divTmp = colNum;
do {
divTmp -= order; sub += order; order *= 26;
divTmp = (divTmp - (divTmp % 26)) / 26;
} while(divTmp > 0);
var symbols = "0123456789abcdefghijklmnopqrstuvwxyz";
var tr = c => symbols[symbols.indexOf(c)+10];
return Number(colNum-sub).toString(26).split('').map(c=>tr(c)).join('');
}
This can handle any number JS can handle, I think.
Explanation:
Since this is not base26, we need to substract the base times order for each additional symbol ("digit"). So first we count the order of the resulting number, and at the same time count the number to substract. And then we convert it to base 26 and substract that, and then shift the symbols to A-Z instead of 0-P.
Anyway, this question is turning into a code golf :)
Java Apache POI
String columnLetter = CellReference.convertNumToColString(columnNumber);
This will cover you out as far as column AZ:
=iferror(if(match(A2,$A$1:$AZ$1,0)<27,char(64+(match(A2,$A$1:$AZ$1,0))),concatenate("A",char(38+(match(A2,$A$1:$AZ$1,0))))),"No match")
A function to convert a column index to letter combinations, recursively:
function lettersFromIndex(index, curResult, i) {
if (i == undefined) i = 11; //enough for Number.MAX_SAFE_INTEGER
if (curResult == undefined) curResult = "";
var factor = Math.floor(index / Math.pow(26, i)); //for the order of magnitude 26^i
if (factor > 0 && i > 0) {
curResult += String.fromCharCode(64 + factor);
curResult = lettersFromIndex(index - Math.pow(26, i) * factor, curResult, i - 1);
} else if (factor == 0 && i > 0) {
curResult = lettersFromIndex(index, curResult, i - 1);
} else {
curResult += String.fromCharCode(64 + index % 26);
}
return curResult;
}
function lettersFromIndex(index, curResult, i) {
if (i == undefined) i = 11; //enough for Number.MAX_SAFE_INTEGER
if (curResult == undefined) curResult = "";
var factor = Math.floor(index / Math.pow(26, i));
if (factor > 0 && i > 0) {
curResult += String.fromCharCode(64 + factor);
curResult = lettersFromIndex(index - Math.pow(26, i) * factor, curResult, i - 1);
} else if (factor == 0 && i > 0) {
curResult = lettersFromIndex(index, curResult, i - 1);
} else {
curResult += String.fromCharCode(64 + index % 26);
}
return curResult;
}
document.getElementById("result1").innerHTML = lettersFromIndex(32);
document.getElementById("result2").innerHTML = lettersFromIndex(6800);
document.getElementById("result3").innerHTML = lettersFromIndex(9007199254740991);
32 --> <span id="result1"></span><br> 6800 --> <span id="result2"></span><br> 9007199254740991 --> <span id="result3"></span>
In python, there is the gspread library
import gspread
column_letter = gspread.utils.rowcol_to_a1(1, <put your col number here>)[:-1]
If you cannot use python, I suggest looking the source code of rowcol_to_a1() in https://github.com/burnash/gspread/blob/master/gspread/utils.py
Here's a two liner which works beyond ZZ using recursion:
Python
def col_to_letter(n):
l = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
return col_to_letter((n-1)//26) + col_to_letter(n%26) if n > 26 else l[n-1]
Javascript
function colToLetter(n) {
l = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
return n > 26 ? colToLetter(Math.floor((n-1)/26)) + colToLetter(n%26) : l[n-1]
}
If you need a version directly in the sheet, here a solution:
For the colonne 4, we can use :
=Address(1,4)
I keep the row number to 1 for simplicty.
The above formula returns $D$1 which is not what you want.
By modifying the formula a little bit we can remove the dollar signs in the cell reference.
=Address(1,4,4)
Adding four as the third argument tells the formula that we are not looking for absolute cell reference.
Now the returns is : D1
So you only need to remove the 1 to get the colonne lettre if you need, for example with :
=Substitute(Address(1,4,4),"1","")
That returns D.
This is a way to convert column letters to column numbers.
=mmult(ArrayFormula(ifna(vlookup(substitute(mid(rept(" ",3-len(filter(A:A,A:A<>"")))&filter(A:A,A:A<>""),sequence(1,3),1)," ",""),{char(64+sequence(26)),sequence(26)},2,0),0)*{676,26,1}),sequence(3,1,1,0))
Screenshot of the Google Sheet
Don't use 26 radix. Like below.
const n2c = n => {
if (!n) return '';
// Column number to 26 radix. From 0 to p.
// Column number starts from 1. Subtract 1.
return [...(n-1).toString(26)]
// to ascii number
.map(c=>c.charCodeAt())
.map((c,i,arr)=> {
// last digit
if (i===arr.length-1) return c;
// 10 -> p
else if (arr.length - i > 2 && arr[i+1]===48) return c===49 ? null : c-2;
// 0 -> p
else if (c===48) return 112;
// a-1 -> 9
else if (c===97) return 57;
// Subtract 1 except last digit.
// Look at 10. This should be AA not BA.
else return c-1;
})
.filter(c=>c!==null)
// Convert with the ascii table. [0-9]->[A-J] and [a-p]->[K-Z]
.map(a=>a>96?a-22:a+17)
// to char
.map(a=>String.fromCharCode(a))
.join('');
};
const table = document.createElement('table');
table.border = 1;
table.cellPadding = 3;
for(let i=0, row; i<1380; i++) {
if (i%5===0) row = table.insertRow();
row.insertCell().textContent = i;
row.insertCell().textContent = n2c(i);
}
document.body.append(table);
td:nth-child(odd) { background: gray; color: white; }
td:nth-child(even) { background: silver; }
Simple typescript functional approach
const integerToColumn = (integer: number): string => {
const base26 = (x: number): string =>
x < 26
? String.fromCharCode(65 + x)
: base26((x / 26) - 1) + String.fromCharCode(65 + x % 26)
return base26(integer)
}
console.log(integerToColumn(0)) // "A"
console.log(integerToColumn(1)) // "B"
console.log(integerToColumn(2)) // "C"
Here is a general version written in Scala. It's for a column index start at 0 (it's simple to modify for an index start at 1):
def indexToColumnBase(n: Int, base: Int): String = {
require(n >= 0, s"Index is non-negative, n = $n")
require(2 <= base && base <= 26, s"Base in range 2...26, base = $base")
def digitFromZeroToLetter(n: BigInt): String =
('A' + n.toInt).toChar.toString
def digitFromOneToLetter(n: BigInt): String =
('A' - 1 + n.toInt).toChar.toString
def lhsConvert(n: Int): String = {
val q0: Int = n / base
val r0: Int = n % base
val q1 = if (r0 == 0) (n - base) / base else q0
val r1 = if (r0 == 0) base else r0
if (q1 == 0)
digitFromOneToLetter(r1)
else
lhsConvert(q1) + digitFromOneToLetter(r1)
}
val q: Int = n / base
val r: Int = n % base
if (q == 0)
digitFromZeroToLetter(r)
else
lhsConvert(q) + digitFromZeroToLetter(r)
}
def indexToColumnAtoZ(n: Int): String = {
val AtoZBase = 26
indexToColumnBase(n, AtoZBase)
}
In PowerShell:
function convert-IndexToColumn
{
Param
(
[Parameter(Mandatory)]
[int]$col
)
"$(if($col -gt 26){[char][int][math]::Floor(64+($col-1)/26)})$([char](65 + (($col-1) % 26)))"
}
Here is a 0-indexed JavaScript function without a maximum value, as it uses a while-loop:
function indexesToA1Notation(row, col) {
const letterCount = 'Z'.charCodeAt() - 'A'.charCodeAt() + 1;
row += 1
let colName = ''
while (col >= 0) {
let rem = col % letterCount
colName = String.fromCharCode('A'.charCodeAt() + rem)
col -= rem
col /= letterCount
}
return `${colName}${row}`
}
//Test runs:
console.log(indexesToA1Notation(0,0)) //A1
console.log(indexesToA1Notation(37,9)) //J38
console.log(indexesToA1Notation(5,747)) //ABT6
I wrote it for a web-app, so I'm not 100% sure it works in Google Apps Script, but it is normal JavaScript, so I assume it will.
For some reason I cant get the snippet to show its output, but you can copy the code to some online playground if you like
Here's a zero-indexed version (in Python):
letters = []
while column >= 0:
letters.append(string.ascii_uppercase[column % 26])
column = column // 26 - 1
return ''.join(reversed(letters))
Here is a JS Fiddle.
The script speaks for itself. I just want to point out that it doesn't work. Please take a look and tell me what to do different. Thanks in advance. I have followed what I believe is every rule in javascript programming, but somewhere somehow I must have overlooked something. I have also made an actual working version og the script i PHP. The working PHP is the second script in this post: PHP split string at last number, insert an extra string and merge the new string.
function calTime(x) {
if (x === '') {
x = 54098;
} // Time in seconds
var f = 31536000, // seconds in a year
d = 86400, // seconds in a day
h = 3600, // seconds in an hour
m = 60, // seconds in a minute
xa,
xb,
xc,
xe,
xq,
string,
lb_y = 'year',
lb_ys = 'years',
lb_d = 'day',
lb_ds = 'days',
lb_h = 'hour',
lb_hs = 'hours',
lb_m = 'minute',
lb_ms = 'minutes',
lb_s = 'second',
lb_ss = 'seconds',
lb_and = 'and';
// a = years
var a = x / f;
// To prevent complications using scientific numbers less than 0 ex 7.2341232E-23
var a1 = a.indexOf("E-");
if (a1) {
a = 0;
}
// Split a so we only get the numbers before '.'
var a2 = a.indexOf(".");
if (a2) {
Math.floor(a);
}
// if $a is less or equal to 0 - it is 0
if (a <= 0) {
a = 0;
}
// b = days
var b = (x - (f * a)) / d;
// To prevent complications using scientific numbers less than 0 ex 7.2341232E-23
var b1 = b.indexOf("E-");
if (b1) {
b = 0;
}
// Split b so we only get the numbers before '.'
var b2 = b.indexOf(".");
if (b2) {
Math.floor(b);
}
// if $b is less or equal to 0 - it is 0
if (b <= 0) {
b = 0;
}
// c = hours
var c = (x - (f * a) - (d * b)) / h;
// To prevent complications using scientific numbers less than 0 ex 7.2341232E-23
var c1 = c.indexOf("E-");
if (c1) {
c = 0;
}
// Split c so we only get the numbers before '.'
var c2 = c.indexOf(".");
if (c2) {
Math.floor(c);
}
// if $c is less or equal to 0 - it is 0
if (c <= 0) {
c = 0;
}
// e = minutes
var e = (x - (f * a) - (d * b) - (h * c)) / m;
// Split $e so we only get the numbers before '.'
var e2 = e.indexOf(".");
if (e2) {
Math.floor(e);
}
// if $e is less or equal to 0 - it is 0
if (e <= 0) {
e = 0;
}
// $q = seconds
var q = (x - (f * a) - (d * b) - (h * c) - (m * e));
// Rewrite numbers if below 9
if (a <= 9) {
xa = '0' + a;
} else {
xa = a;
}
if (b <= 9) {
xb = '0' + b;
} else {
xb = b;
}
if (c <= 9) {
xc = '0' + c;
} else {
xc = c;
}
if (e <= 9) {
xe = '0' + e;
} else {
xe = e;
}
if (q <= 9) {
xq = '0' + q;
} else {
xq = q;
}
// Rewrite labels
if (a <= 1) {
lb_ys = lb_y;
}
if (b <= 1) {
lb_ds = lb_d;
}
if (c <= 1) {
lb_hs = lb_h;
}
if (e <= 1) {
lb_ms = lb_m;
}
if (q <= 1) {
lb_ss = lb_s;
}
// if == 0 - do not show
if (a === 0) {
a = '';
} else {
a = a + ' ' + lb_ys;
}
if (b === 0) {
b = '';
} else {
b = b + ' ' + lb_ds;
}
if (c === 0) {
c = '';
} else {
c = c + ' ' + lb_hs;
}
if (e === 0) {
e = '';
} else {
e = e + ' ' + lb_ms;
}
if (q === 0) {
q = '';
} else {
q = q + ' ' + lb_ss;
}
var time = [a, b, c, e, q];
time = time.filter(Number);
var count = time.count();
var last = time[time.length - 1];
if (count == 1) {
string = last;
} else if (count === 0) {
string = '<i>No Time described</i>';
} else {
string = time.join(', ') + ' ' + lb_and + ' ' + last;
}
return string;
}
document.getElementById("demo").innerHTML = calTime(83200);
I'll try to identify everything technically wrong with the script in one place.
Incorrect time calculations
There's 86400 seconds in a day and 31536000 seconds in a 365-day year. You'll usually see people do things like this if they want to not worry about values:
var minutes = 60;
var hours = 60 * 60;
var days = 24 * 60 * 60;
var years = 365 * 24 * 60 * 60;
Use of indexOf() on objects (in this case, numbers) that don't support that method.
Others have pointed this out in comments and answers, but basically, convert your numbers to strings if you're going to call string methods on them:
num = num + "";
numIndex = (num + "").indexOf("foo");
Not properly checking the return value of indexOf()
indexOf() returns the index (from 0) of the location where the string starts. If the string is not found, it returns -1. In several locations, you are doing something like:
var a2 = a.indexOf("E-");
if (a2) {
a = 0;
}
a2, in this case, will be -1 if it does not match scientific notation. The only integer value considered false is 0. The -1 value is therefore always considered true and you're always setting your years, days and hours to 0, regardless of whether they are in a scientific notation format or not.
Not considering case in the scientific notation format
In my browser, very small, close to zero values look like this:
7.888609052210118e-31
Your search won't match this value. This may not even matter, given your logic. Is there any reason just not to always use Math.floor()? Your JS floating-point problem is going to be a problem any way you slice it.
Not using the return value of Math.floor()
In several locations, you do something like the following:
Math.floor(a);
Then you go on to assume that a has assumed its floored value. You need to do the following to make that happen:
a = Math.floor(a);
Setting all of your time components to strings, and then filtering them by Number
You explicitly store string formats (e.g. 23 hours, 6 minutes) in your time array, but then, you filter the time array by Number. I think you're trying to filter out blank strings, which is what you set the time values to when they're 0. Pass a function to filter() to filter those blank entries out, like so:
time.filter(function(x) { return x === "" ? false : true; });
count() is not a method of Array
You're probably looking for length, which you actually use correctly below that.
You join the entire time array together, then tack on the last item in the array again
I'll let you solve this. You don't want to duplicate the last item, and you probably also want to handle the case where the time evenly divides into one of your categories
Those are the technical things wrong with your script. Another complete answer could be written on more elegant ways in which to accomplish what you're trying to do.
To add to the answers already given, the main style problem with your code is that there is a lot of unnecessary repetition, which you could get rid of using a loop and/or function.
Having said that, the maths can be greatly simplified, and there doesn't seem to be any need for all the stuff that searches for 'E' or '.', in which case it's probably not worth using a loop; in the suggestion below I use a loop only to add the labels. Another hint is to use descriptive variable names (hours, minutes, instead of a, b) where possible, to make your code more readable.
http://jsfiddle.net/m54Du/16/
function calTime(seconds) {
if (seconds === '') {
seconds = 54098;
} // Time in seconds
seconds = Math.floor(seconds);
if (isNaN(seconds) || seconds <= 0) {
return '<i>No time described</i>';
}
var minutes = Math.floor(seconds / 60),
hours = Math.floor(minutes / 60),
days = Math.floor(hours / 24),
years = Math.floor(days / 365), // assuming not leap!
timeData = [years, days % 365, hours % 24, minutes % 60, seconds % 60],
pluralLabels = ['years', 'days', 'hours', 'minutes' , 'seconds'],
singularLabels = ['year', 'day', 'hour', 'minute', 'second'],
time = [];
for (var i = 0; i < timeData.length; i++) {
if (timeData[i] > 1) {
time.push(timeData[i] + ' ' + pluralLabels[i]);
}
else if (timeData[i] > 0) {
time.push(timeData[i] + ' ' + singularLabels[i]);
}
}
var last = time.pop();
return time.length ? time.join(', ') + ' and ' + last : last;
}
document.getElementById("demo").innerHTML = calTime(83200);
Here's an alternative making more use of a loop to do the maths.
Your variables a, b, etc. are numbers. indexOf is a method for strings in JavaScript.
One way of fixing this would be to turn your numbers into strings, like:
a = a + "";
which removes the indexOf() errors.
For the rest of the errors, I think you're also misusing functions. For example, you use a count() method, which doesn't appear to exist in JavaScript.
I have a number assigned to a variable, like that:
var myVar = 1234;
Now I want to get the second digit (2 in this case) from that number without converting it to a string first. Is that possible?
So you want to get the second digit from the decimal writing of a number.
The simplest and most logical solution is to convert it to a string :
var digit = (''+myVar)[1];
or
var digit = myVar.toString()[1];
If you don't want to do it the easy way, or if you want a more efficient solution, you can do that :
var l = Math.pow(10, Math.floor(Math.log(myVar)/Math.log(10))-1);
var b = Math.floor(myVar/l);
var digit = b-Math.floor(b/10)*10;
Demonstration
For people interested in performances, I made a jsperf. For random numbers using the log as I do is by far the fastest solution.
1st digit of number from right → number % 10 = Math.floor((number / 1) % 10)
1234 % 10; // 4
Math.floor((1234 / 1) % 10); // 4
2nd digit of number from right → Math.floor((number / 10) % 10)
Math.floor((1234 / 10) % 10); // 3
3rd digit of number from right → Math.floor((number / 100) % 10)
Math.floor((1234 / 100) % 10); // 2
nth digit of number from right → Math.floor((number / 10^n-1) % 10)
function getDigit(number, n) {
return Math.floor((number / Math.pow(10, n - 1)) % 10);
}
number of digits in a number → Math.max(Math.floor(Math.log10(Math.abs(number))), 0) + 1 Credit to: https://stackoverflow.com/a/28203456/6917157
function getDigitCount(number) {
return Math.max(Math.floor(Math.log10(Math.abs(number))), 0) + 1;
}
nth digit of number from left or right
function getDigit(number, n, fromLeft) {
const location = fromLeft ? getDigitCount(number) + 1 - n : n;
return Math.floor((number / Math.pow(10, location - 1)) % 10);
}
Get rid of the trailing digits by dividing the number with 10 till the number is less than 100, in a loop. Then perform a modulo with 10 to get the second digit.
if (x > 9) {
while (x > 99) {
x = (x / 10) | 0; // Use bitwise '|' operator to force integer result.
}
secondDigit = x % 10;
}
else {
// Handle the cases where x has only one digit.
}
A "number" is one thing.
The representation of that number (e.g. the base-10 string "1234") is another thing.
If you want a particular digit in a decimal string ... then your best bet is to get it from a string :)
Q: You're aware that there are pitfalls with integer arithmetic in Javascript, correct?
Q: Why is it so important to not use a string? Is this a homework assignment? An interview question?
You know, I get that the question asks for how to do it without a number, but the title "JavaScript: Get the second digit from a number?" means a lot of people will find this answer when looking for a way to get a specific digit, period.
I'm not bashing the original question asker, I'm sure he/she had their reasons, but from a search practicality standpoint I think it's worth adding an answer here that does convert the number to a string and back because, if nothing else, it's a much more terse and easy to understand way of going about it.
let digit = Number((n).toString().split('').slice(1,1))
// e.g.
let digit = Number((1234).toString().split('').slice(1,1)) // outputs 2
Getting the digit without the string conversion is great, but when you're trying to write clear and concise code that other people and future you can look at really quick and fully understand, I think a quick string conversion one liner is a better way of doing it.
function getNthDigit(val, n){
//Remove all digits larger than nth
var modVal = val % Math.pow(10,n);
//Remove all digits less than nth
return Math.floor(modVal / Math.pow(10,n-1));
}
// tests
[
0,
1,
123,
123456789,
0.1,
0.001
].map(v =>
console.log([
getNthDigit(v, 1),
getNthDigit(v, 2),
getNthDigit(v, 3)
]
)
);
This is how I would do with recursion
function getDigits(n, arr=[]) {
arr.push(n % 10)
if (n < 10) {
return arr.reverse()
}
return getDigits(Math.floor(n/10),arr)
}
const arr = getDigits(myVar)
console.log(arr[2])
I don’t know why you need this logic, but following logic will get you the second number
<script type="text/javascript">
var myVal = 58445456;
var var1 = new Number(myVal.toPrecision(1));
var var2 = new Number(myVal.toPrecision(2));
var rem;
rem = var1 - var2;
var multi = 0.1;
var oldvalue;
while (rem > 10) {
oldvalue = rem;
rem = rem * multi;
rem = rem.toFixed();
}
alert(10-rem);
</script>
function getDigit(number, indexFromRight) {
var maxNumber = 9
for (var i = 0; i < indexFromRight - 2; i++) {
maxNumber = maxNumber * 10 + 9
}
if (number > maxNumber) {
number = number / Math.pow(10, indexFromRight - 1) | 0
return number % 10
} else
return 0
}
Just a simple idea to get back any charter from a number as a string or int:
const myVar = 1234;
String(myVar).charAt(1)
//"2"
parseInt(String(myVar).charAt(1))
//2
you can use this function
index = 0 will give you the first digit from the right (the ones)
index = 1 will give you the second digit from the right (the tens)
and so on
const getDigit = (num, index) => {
if(index === 0) {
return num % 10;
}
let result = undefined;
for(let i = 1; i <= index; i++) {
num -= num % 10;
num /= 10;
result = num % 10;
}
return result;
}
for Example:
getDigit(125, 0) // returns 5
gitDigit(125, 1) // returns 2
gitDigit(125, 2) // returns 1
gitDigit(125, 3) // returns 0
function left(num) {
let newarr = [];
let numstring = num.split('[a-z]').join();
//return numstring;
const regex = /[0-9]/g;
const found = numstring.match(regex);
// return found;
for(i=0; i<found.length; i++){
return found[i];
}
}
//}
console.log(left("TrAdE2W1n95!"))
function getNthDigit(n, number){
return ((number % Math.pow(10,n)) - (number % Math.pow(10,n-1))) / Math.pow(10,n-1);
}
Explanation (Number: 987654321, n: 5):
a = (number % Math.pow(10,n)) - Remove digits above => 54321
b = (number % Math.pow(10,n-1)) - Extract digits below => 4321
a - b => 50000
(a - b) / 10^(5-1) = (a - b) / 10000 => 5
var newVar = myVar;
while (newVar > 100) {
newVar /= 10;
}
if (newVar > 0 && newVar < 10) {
newVar = newVar;
}
else if (newVar >= 10 && newVar < 20) {
newVar -= 10;
}
else if (newVar >= 20 && newVar < 30) {
newVar -= 20;
}
else if (newVar >= 30 && newVar < 40) {
newVar -= 30;
}
else if (newVar >= 40 && newVar < 50) {
newVar -= 40;
}
else if (newVar >= 50 && newVar < 60) {
newVar -= 50;
}
else if (newVar >= 60 && newVar < 70) {
newVar -= 60;
}
else if (newVar >= 70 && newVar < 80) {
newVar -= 70;
}
else if (newVar >= 80 && newVar < 90) {
newVar -= 80;
}
else if (newVar >= 90 && newVar < 100) {
newVar -= 90;
}
else {
newVar = 0;
}
var secondDigit = Math.floor(newVar);
That's how I'd do it :)
And here's a JSFiddle showing it works :) http://jsfiddle.net/Cuytd/
This is also assuming that your original number is always greater than 9... If it's not always greater than 9 then I guess you wouldn't be asking this question ;)