Truncate a String in Javascript - javascript

I'm trying to accomplish the following :
Truncate a string (first argument) if it is longer than the given maximum string length (second argument). Return the truncated string with a ... ending.
Note that inserting the three dots to the end will add to the string length.
However, if the given maximum string length num is less than or equal to 3, then the addition of the three dots does not add to the string length in determining the truncated string.
The code I have will only pass test if my num >2, otherwise it fails.
function truncateString(str, num) {
// Clear out that junk in your trunk
var trunString = "";
if (str.length > num) {
trunString = str.slice(0, (num -3)) + "...";
return trunString;
}
return str;
}
truncateString("A-", 1);
Found a solution for this:
var trunString = "";
if (str.length > num && num >= 4 ) {
trunString = str.slice(0, (num - 3)) + "...";
return trunString;
}
else if (str.length > num && num <= 3) {
trunString = str.slice(0, (num)) + "...";
return trunString;
}

You could use a conditional (ternary) operator ?:
function truncateString(str, num) {
return str.length > num ?
str.slice(0, num > 3 ? num - 3 : num) + "..." :
str;
}
console.log(truncateString("Abcdefghijk", 5));
console.log(truncateString("A-", 1));
console.log(truncateString("Alpha", 5));
console.log(truncateString("Beta", 5));
console.log(truncateString("Epsilon", 3));

function truncateString(str, num) {
// clear out that junk in your trunk
var trunString = '';
if (str.length && str.length > num) {
trunString = str.slice(0, num - 1) + '…';
return trunString;
}
return str;
}
truncateString("A-", 1);
P.S.: read also #techfoobar comment, using &hellip HTML entity will help...

This is my solution, hoping to help you
function truncate(str, num) {
// Clear out that junk in your trunk
var newStr;
if(num>3&&num<str.length){
newStr=str.slice(0,num-3)+"...";
}else if(num>=str.length){
newStr=str;
}else{
newStr=str.slice(0,num)+"...";
}
return newStr;
}
truncate("A-tisket a-tasket A green and yellow basket", "A-tisket a-tasket A green and yellow basket".length);

Related

Javascript numbers - split/slice Prime

I am completely new to Javascript and trying to solve a simple problem now for more than two weeks and still not getting it(please help).
TASK ::::
Read a 4 digit Number e.g. 5678
Write a function
Split/separate the numbers and than build (5678, 567, 56, 5), than check if the numbers(5678, 567, 56, 5) are Prime numbers.
Give in Console/Result if 5678 a prime number or not, 567 a prime number or not and so on.
Check "if all numbers are Prime" than show result "All prime" if not show result "Not all prime".
Trying to solve the problem with (if else) but not really getting it, because i know very less about Javascript (arrays, string, split, slice) yet.
please help me understand. Thanks.
var a = 123456789;
var b = a.toString().length; //<<--->> ANTWORT: 9
document.write('ANTWORT: ',a );
for (i=0; i<b; i++) {
var x = a.toString().slice(0, -i);
document.write(x, ",");
}
function isPrime{
for(var i = 2; i < a; i++);
if(num % i === 0) return false;
return num > 1;
}
//integer is a string at the moment
integer = prompt("Enter a integer: ");
//initialize array for dictionary
dictArray = [];
stuff = document.getElementById("stuff");
//loop through all values of the string
for (var i = integer.length; i > 0; i--)
{
//take a substring from 0 to the ith char and turn it into an int
num = parseInt(integer.substring(0, i));
//add a dictionary to the array the tells what the number is
//and if it was prime or not as a bool
dictArray.push({"num": num, "prime": isprime(num)});
(dictArray[integer.length - i]["prime"]) ? stuff.innerHTML += "<br>" + num + " is prime." : stuff.innerHTML += "<br>" + num + " is not prime.";
}
function isprime(num)
{
if (num <= 3) return num >= 1;
if ((num % 2 === 0) || (num % 3 === 0)) return false;
let count = 5;
while (Math.pow(count, 2) <= num) {
if (num % count === 0 || num % (count + 2) === 0) return false;
count += 6;
}
return true;
}
//print the array
(dictArray.find(x => !x.prime) == undefined) ? stuff.innerHTML += "<br>All prime!" : stuff.innerHTML += "<br>Not all prime!";
//console.log(dictArray);
<div id="stuff">
</div>

freeCodeCamp - Truncating a String. Exercise

I am trying to solve the problem Basic Algorithm Scripting: Truncate a String; but is not working properly...
let truncateString = (str, num) => {
return (str.length <= num ? str : str.slice(0, num > 3 ? num - 3 : num) + '...');
};
truncateString("A-tisket a-tasket A green and yellow basket", 8);
// running tests
truncateString("A-tisket a-tasket A green and yellow basket", 8) should return "A-tisket...".
truncateString("Peter Piper picked a peck of pickled peppers", 11) should return "Peter Piper...".
// tests completed
It's even simpler than your code - change your ternary operator a little:
let truncateString = (str, num) => str.length > num ? str.slice(0, num) + "..." : str;
console.log(truncateString("A-tisket a-tasket A green and yellow basket", 8));
console.log(truncateString("Peter Piper picked a peck of pickled peppers", 11));
Here: (works)
let truncateString = (str, num) => {
return (str.length <= num ? str : str.slice(0, num) + '...');
};
let truncateString = (str, num) => {
const x = str.length,
y = num,
min = y ^ ((x ^ y) & -(x < y));
return str.slice(0, min) + (!(y-min) && '...');
};

How can I make this to remember the last modification statement

How can I make this to remember the last modification statement.
Bacause this code is Always reinitialize the str variable.
But I have to make a loop what is add plus one "*" to my str. This is the reason why I want to "save" the previous statement.
Above I posted the test results.
function padIt(str, n) {
do {
if (n % 2 === 0) {
str + "*";
}
else {
str = "*" + str;
}
} while (n > 5)
return str;
}
I get this:
Test Passed: Value == '\'*a\''
Expected: '\'*a*\'', instead got: '\'a\''
Expected: '\'**a*\'', instead got: '\'*a\''
Expected: '\'**a**\'', instead got: '\'a\''
You are missing += in your if block. It should be str += "*";
function padIt(str, n) {
do {
if (n % 2 === 0) {
str += "*";
} else {
str = "*" + str;
}
} while (n > 5)
return str;
}
I think you really intended the padIt function to be recursive. If so, we can attempt such a solution by adding some padding on each side, in each recursive call. The base case occurs when the n counter reaches one, in which case we just return the cumulatively built padded string.
padIt = function(str, n) {
if (n === 1) return str;
if (n%2 === 0) {
return padIt(str + "*", n-1);
}
else {
return padIt("*" + str, n-1);
}
}
console.log(padIt("a", 5));

Check how many times a char appears in a string

Simply trying to find how many times a given character appears in a string but I can't solve it any other way then this simple for-loop. Is there a method that would solve this quicker or more eloquently other than using Regex?
function countCharacter(str, char) {
var count = 0;
for(var i = 0; i < str.length; i++){
if(str.charAt(i) === char)
count++;
}
return count;
}
There are many possible ways are available in the market.
I am adding a few of them.
Method 1:
str = "The man is as good as his word"
str.split('a')
output: (4) ["The m", "n is ", "s good ", "s his word"]
str.split('a').length - 1
output: 3
Method 2:
str = "The man is as good as his word"
str.split('').map( function(char,i)
{ if(char === 'a')
return i;
}
).filter(Boolean)
Output: (3) [5, 11, 19]
str.split('').map( function(char,i)
{ if(char === 'a')
return i;
}
).filter(Boolean).length
ouput: 3
Edit: As per comment we can also make use of filter().
str.split('').filter(function(char, i){
if(char == 'a'){
return i;
}
})
output: (3) ["a", "a", "a"]
str.split('').filter(function(char, i){
if(char == 'a'){
return i;
}
}).length
output: 3
----edited by adding more cases from answers----
there are several ways, you can use split/for/regex/reduce/indexOf like this:
function countCharacter_reduce(str, ch) {
return Array.prototype.reduce.call(str, (prev, cur) => cur === ch && ++prev && prev, 0);
}
function countCharacter_split(str, ch) {
return str.split(ch).length - 1;
}
function countCharacter_for(str, ch) {
for (var count = 0, ii = 0; ii < str.length; ii++) {
if (str[ii] === ch)
count++;
}
return count;
}
function countCharacter_regex(str, ch) {
return str.length - str.replace(new RegExp(ch, 'g'), '').length;
}
function countCharacter_indexOf(str, char) {
var start = 0;
var count = 0;
while ((start = str.indexOf(char, start) + 1) !== 0) {
count++;
}
return count;
}
performance of them by running 1,000,000 times on counting '/' in a string.
-- case1: running 1000000 times on ( 'this/is/a/path/with/extension', '/' )
countCharacter_reduce: 2389.880ms
countCharacter_regex: 496.251ms
countCharacter_split: 114.709ms
countCharacter_for: 152.012ms
countCharacter_indexOf: 90.330ms
-- case2: running 1000000 times on ( '////////////', '/' )
countCharacter_reduce: 1138.051ms
countCharacter_regex: 619.886ms
countCharacter_split: 121.945ms
countCharacter_for: 74.542ms
countCharacter_indexOf: 204.242ms
Conclusion ('>' means 'has better performance'):
for|split|indexOf > regex > reduce.
furthermore,
if the string contains more searching characters (like case2),
for>split>indexOf,
otherwise (like case1)
indexOf > split > for.
BTW: you can change the for indexOf method to fit multi-characters search (example is single character)
using reduce:
function countCharacter(str, char) {
return str.split('').reduce((a, x) => x === char ? ++a : a, 0);
}
I guess this involves regex which you wanted to avoid but it's pretty quick:
function countCharacter(str, char) {
return str.length - str.replace(new RegExp(char,"g"),"").length;
}
You can also try the str.split(char).length-1 approach suggested by Jaromanda.
Or, go all out with some fun recursion (pass 0 to startingFrom):
function countCharacter(str, char, startingFrom) {
var idx = str.indexOf(char, startingFrom);
return idx == -1 ? 0 : 1 + countCharacter(str, char, idx + 1);
}
You can get rid of the annoying extra argument at the cost of some efficiency:
function countCharacter(str, char) {
var idx = str.indexOf(char);
return idx == -1 ? 0 : 1 + countCharacter(str.substr(idx+1), char);
}
And here's a version optimized for speed (this is about 3 times faster on my browser than your original and much faster than the regex versions, according to jsperf):
function countCharacter(str, char) {
var start = 0;
var count = 0;
while((start = str.indexOf(char, start)+1) !== 0) {
count++;
}
return count;
}
Note that the indexOf approaches will generally be substantially faster than manually iterating through the string. See jsperf
Here you go. One line code
"hello".match(new RegExp('l','g')).length
replace 'l' with any char here, new RegExp('l','g').
that is
str.match(new RegExp(char,'g')).length
Have you thought of using the split() method? How about this -
function myFunction(str, char) {
return string.split(char).length - 1
}
Let me know what you think of this solution.

Convert column index into corresponding column letter

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))

Categories