UPDATED BELOW
What I'm trying to do is iterate through an array in chunks, alternating the direction of iteration from chunk to chunk. Confused? So am I. For example, if I want to loop thru an array with 25 elements, but I want to do it in this order: 0, 1, 2, 3, 4, 9, 8, 7, 6, 5, 10, 11, 12, 13, 14, 19, 18, 17, 16, 15, 20, 21, 22, 23, 24, what would be the most efficient way of doing this? I'm looking for something scalable because the array I'm working with now is actually 225 elements and I want to traverse it in 15 element chunks, but that may change at some point. So far, the only method I've figured out that actually works is to hard-wire the iteration order into a second array and then iterate through that in the normal way to get the indices for the original array. But that sucks. Any help would be greatly appreciated.
#Bergi asked for some sample code. Please don't beat me up too much. I'm still a noob:
function zeroPadNumber(theNumber, thePadding) {
var thePaddedNumber = thePadding.substring(0, (thePadding.length - theNumber.length)) + theNumber;
return thePaddedNumber;
}
var thisTile = 225;
var waveLoop = 15;
function mosaicWave() {
var theStartNum = thisTile;
for (w = 0; w < 15; w++) {
var theNum = theStartNum - w;
var theNumString = String(theNum);
var thePaddedNum = zeroPadNumber(theNumString, "000");
var theImgName = "sm_" + thePaddedNum;
var theNewSrc = theImgFolder + theImgName + "bg.gif";
document.images[theImgName].src = theNewSrc;
thisTile = theNum - 1;
if (waveLoop < 15) {
var prevStartTile = theStartNum + 15;
var thePrevNum = prevStartTile - w;
var thePrevNumString = String(thePrevNum);
var thePrevPaddedNum = zeroPadNumber(thePrevNumString, "000");
var thePrevName = "sm_" + thePrevPaddedNum;
var thePrevSrc = theImgFolder + thePrevName + ".gif";
document.images[thePrevName].src = thePrevSrc;
}
}
if (waveLoop == 1) {
var lastWave = function() {
var theStartNum = 15;
for (c = 0; c < 15; c++) {
var theNum = theStartNum - c;
var theNumString = String(theNum);
var thePaddedNum = zeroPadNumber(theNumString, "000");
var theImgName = "sm_" + thePaddedNum;
var theNewSrc = theImgFolder + theImgName + ".gif";
document.images[theImgName].src = theNewSrc;
}
}
setTimeout(lastWave, 100);
waveLoop = 15;
thisTile = 225;
} else {
waveLoop--;
setTimeout(mosaicWave, 100);
}
}
This snippet does a different animation. It starts at the lower right corner of the matrix and "turns on" the 15 tiles in the bottom row. then it moves up a row, turns on the tiles in that row and turns off the tiles in the previous row. And so forth until the top row is turned on then off. not very far off really from the top-down serpentine effect I'm trying to achieve in the new function. The reversing order on each row was the main thing stumping me. That being said, any suggestions about optimizing the above code would also be greatly appreciated.
UPDATE 1:
To me, this seems like it should work, but it doesn't. Can anyone spot the problem?
var loopRange = 225;
var blockRange = 15;
var theDirection = 1;
var weaveLoop = 0;
function mosaicWeave() {
var curObj, curSrc, lastObj, lastSrc;
var toggleLeadTile = function() {
alert(curSrc);
curObj.src = curSrc;
};
var toggleLastTile = function() {
lastObj.src = lastSrc;
};
while (weaveLoop < loopRange) {
imgNum = weaveLoop + 1;
imgName = "sm_" + zeroPadNumber(String(imgNum), "000");
if (imgNum < 15) {
//handle first row
curObj = document.images[imgName];
curSrc = theImgFolder + imgName + "bg.gif";
window.setTimeout(toggleLeadTile, 100);
} else if (imgNum == 225) {
//handle last row
curObj = document.images[imgName].src;
curSrc = theImgFolder + imgName + "bg.gif";
window.setTimeout(toggleLeadTile, 100);
for (i = 211; i < 226; i++) {
lastImgName = "sm_" + ((weaveLoop + 1) - 15);
lastObj = document.images[lastImgName];
lastSrc = theImgFolder + lastImgName + ".gif";
window.setTimeout(toggleLastTile, 100);
}
} else {
//handle middle rows
lastImgName = "sm_" + ((weaveLoop + 1) - 15);
curObj = document.images[imgName];
curSrc = theImgFolder + imgName + "bg.gif";
lastObj = document.images[lastImgName];
lastSrc = theImgFolder + lastImgName + ".gif";
window.setTimeout(toggleLeadTile, 100);
window.setTimeout(toggleLastTile, 100);
}
if (weaveLoop % blockRange == (theDirection == -1 ? 0 : blockRange - 1)) {
theDirection *= -1;
weaveLoop += blockRange;
} else {
weaveLoop += theDirection;
}
}
}
UPDATE 2:
Thanks for everyone's input. This works:
var resetLoop = 1;
var weaveArray = new Array(225);
var weaveRange = 15, weaveDirection = 1, weaveIndex = 0, wInitLoop = 0;
function mosaicWeave() {
while (weaveIndex < 225) {
weaveArray[wInitLoop] = weaveIndex + 1;
if (weaveIndex % weaveRange == (weaveDirection == -1 ? 0 : weaveRange - 1)) {
weaveDirection *= -1;
weaveIndex += weaveRange;
} else {
weaveIndex += weaveDirection;
}
wInitLoop++;
}
mWeaveOn();
}
function mWeaveOff() {
var theNumString = String(weaveArray[resetLoop - 16]);
var theImgName = "sm_" + zeroPadNumber(theNumString, "000");
document.images[theImgName].src = "images/" + theImgName + ".gif";
mosaicArray[resetLoop - 1] = 0;
resetLoop++;
if (resetLoop < 226) {
setTimeout(mWeaveOn, 25);
} else if (resetLoop > 225 && resetLoop <= 240) {
setTimeout(mWeaveOff, 25);
} else {
resetLoop = 1;
}
}
function mWeaveOn() {
var theNumString = String(weaveArray[resetLoop - 1]);
var theImgName = "sm_" + zeroPadNumber(theNumString, "000");
document.images[theImgName].src = "images/" + theImgName + "bg.gif";
mosaicArray[resetLoop - 1] = 1;
if (resetLoop < 16) {
resetLoop++;
setTimeout(mWeaveOn, 25);
} else {
setTimeout(mWeaveOff, 25);
}
}
Does anyone have an opinion on if there is a more efficient way of doing this? Or a heads up on how this might break on different platforms/browsers or under different circumstances? Thanks again.
var arr = [0,1,2,3,4,5,6,7,8,9,10,11,11,13,14,15,16,17,18,19,20,21,22,23,24],
i = 0,
j = arr.length,
tmp,
chunk = 5;
while(i < j) {
tmp = arr.slice(i, i+=chunk);
if ((i / chunk) % 2 == 0) {
tmp = tmp.reverse();
}
console.log(tmp);
}
The demo.
This is a flexible solution where you can change the blocksize as you need it.
var index, max = 25;
for (var i = 0; i < max; i++) {
if (parseInt(i / 5) % 2)
index = parseInt(i / 5)*5 + 4 - i % 5;
else
index = i;
// use index as array index
foo(index);
}
Fiddle
If you have always a multiple of five, you could hard code the iteration over five elements and do an outer loop which counts to max/5 and switch to the right hardcoded iteration.
var index, max = 25;
for ( var i=0; i<max/5; i++) {
if (i%2) {
foo(i*5+4);
foo(i*5+3);
foo(i*5+2);
foo(i*5+1);
foo(i*5+0);
} else {
foo(i*5+0);
foo(i*5+1);
foo(i*5+2);
foo(i*5+3);
foo(i*5+4);
}
}
Fiddle
I guess the simplest and clearest solution would be to nest two loos:
var arr = new Array(25),
chunksize = 5;
for (var i=0; i<arr.length; i+=chunksize)
if (i % (chunksize*2))
for (var j=i+chunksize-1; j>=i; j--)
exec(j);
else
for (var j=i; j<i+chunksize; j++)
exec(j);
However, you could also go with only one loop and loopcounter. In the right spots (4, 5, 14, 15, …) the direction (increment/decrement) would change and the counter jumps one chunksize (4→9, 5→10, 14→19, …):
var arr = new Array(25),
chunksize = 5;
var dir = 1,
i = 0;
while (i<arr.length) {
exec(i); // or whatever you need to do
if (i % chunksize == (dir==-1 ? 0 : chunksize - 1)) {
dir *= -1;
i += chunksize;
} else
i += dir;
}
or, in one for-statement:
for (var dir=1, i=0; i<arr.length; i+= (i+1)%chunksize == (dir==-1) ? (dir*=-1) && chunksize : dir)
exec(i); // or whatever you need to do
This function accepts an array and a blocksize (in your example, 5)
function forwardAndBack(arr, blocksize){
var i, j, l = arr.length ;
for (i = 0 ; i < l ; i++){
if (i % (2 * blocksize) > (blocksize - 1)){
j = i + (blocksize - (2*(i % blocksize)))-1 ;
}
else {
j = i ;
}
arr[j] && myFunction(arr[j]) ; // In case you've gone too high
}
}
Used like this:
var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24] ;
var result = [] ;
function myFunction(x){result.push(x)} ;
forwardAndBack(arr, 5);
console.log(result) ; // returns [0, 1, 2, 3, 4, 9, 8, 7, 6, 5, 10, 11, 12, 13, 14, 19, 18, 17, 16, 15, 20, 21, 22, 23, 24]
Related
You have a matrix N*N. You move from trough the matrix by spiral. Now that you know how many steps you have taken, you want to know your position in the matrix.
Input/Output
[input] integer size
The size of the room.
[input] integer steps
The number of steps you have taken(1-based).
[output] an integer array
Array of length two, describing your position in the room(0-based).
Example
For size = 5 and steps = 20, the output should be [2, 3].
Your path can be shown as the following figure:
[ 1, 2, 3, 4, 5],
[16, 17, 18, 19, 6],
[15, x, x, 20, 7],
[14, x, x, x, 8],
[13, 12, 11, 10, 9]
The 20th step brought you to the second line and third column (0-based), so the answer is [2, 3].
I build a solution that can build such matrix for me
const initMatrix = size => {
const matrix = [];
for (var i = 0; i < size; i++) {
matrix[i] = new Array(size);
}
return matrix;
};
const blindfolded = (size, steps) => {
const matrix = initMatrix(size);
let nc = size;
let num = 1;
for (let z = 0; z < nc; z++) {
for (let i = z; i < nc; i++) {
matrix[z][i] = num;
num++;
}
for (let i = z + 1; i < nc; i++) {
matrix[i][nc - 1] = num;
num++;
}
for (let i = nc - 2; i >= z; i--) {
matrix[nc - 1][i] = num;
num++;
}
for (let i = nc - 2; i >= z + 1; i--) {
matrix[i][z] = num;
num++;
}
nc--;
}
for (let i = 0; i < matrix.length; i++) {
for (let j = 0; j < matrix[i].length; j++) {
console.log(matrix[i][j]);
}
}
};
blindfolded(7, 1);
But probably there should be another more optimal algorithm
This can be done in constant time. The spiral looks like this:
To find number of coils for a given steps, one need to
ceil(sqrt(steps)/2 - 0.5) - 1 (1)
Side length on i-th level is 2*i+1, we start count from zero.
Suppose steps = 64 and center is (5,5). Applying (1) one can find that number of full levels is three, so we are at (8,8+1) = (8,9) (bold black dot), and we did 49 steps for it. Then, side length on 4-th level is 2*4+1=9, so subtract 7 vertically, subtract 8, and we don't have any more steps to use, so finally we are at (1,1).
Somehow this is not surprising, because we went through square 8x8.
function calc() {
var steps = window.steps.value;
if (steps <= 0) return "must be positive";
var ci = 5, cj = 5;
if (steps == 1) return ci + ", " + cj;
var level = Math.ceil(Math.sqrt(steps)/2 - 0.5) - 1;
var ri = ci + level;
var rj = cj + level + 1;
var sideFull = 2 * level + 1;
steps -= sideFull * sideFull;
var side = sideFull + 2;
{
var verUp = Math.min(steps, side - 2);
steps -= verUp;
ri -= verUp;
}
{
var horLeft = Math.min(steps, side - 1);
steps -= horLeft;
rj -= horLeft;
}
{
var verDown = Math.min(steps, side - 1);
steps += verDown;
ri -= verDown;
}
{
var horRight = Math.min(steps, side - 1);
rj + horRight;
}
return ri + ", " + rj;
}
Center at (5,5), and steps = <input id="steps" />. <button onclick="answer.value = calc();" >Calc</button><br>
Answer: <input id="answer" disabled="disabled" />
Ok, so I'm trying to code a Rectangle with multiple triangle strips joined together. according to:
http://www.corehtml5.com/trianglestripfundamentals.php
You need to take care of the triangles wrapping around when you have more than one row. However using the suggested algorithm in my code example I'm getting incorrect indice results.
Here is my example, with outputs.
I've tried copy/pasting the suggested algorithm but it doesn't seem to be returning correct results.
// Create the Index Points for the buffer array.
var rows=2;
var cols=3;
var grid = rows*cols;
var offset;
var pos = [];
var index = 0;
var mpOffset = 1;
for (var row = 0; row <= rows; ++row)
{
offsetY = row * (mpOffset / rows);
for (var col = 0; col <= cols; ++col)
{
offsetX = col * (mpOffset / cols);
pos[index+0] = (offsetX);
pos[index+1] = (offsetY);
index+=2;
}
}
log.info("pos="+JSON.stringify(pos)); // <-- Correct working good.
log.info("pos.length="+pos.length);
function initializeGrid(cols,rows)
{
var trianglestrip = [];
var RCvertices=2*cols*(rows-1);
var TSvertices=2*cols*(rows-1)+2*(rows-2);
var numVertices=TSvertices;
var j=0;
for(var i = 1; i <= RCvertices; i += 2)
{
trianglestrip[ j ] = (1 +i)/2;
trianglestrip[ j +1 ] = (cols*2 + i + 1) / 2;
if( trianglestrip[ j +1 ] % cols == 0)
{
if( trianglestrip[ j +1 ] != cols && trianglestrip[ j +1 ] != cols*rows )
{
trianglestrip[ j +2 ] = trianglestrip[ j +1 ];
trianglestrip[ j +3 ] = (1 + i + 2) / 2;
j += 2;
}
}
j += 2;
}
return trianglestrip;
}
var triStrip = initializeGrid(cols,rows);
log.info("triStrip="+JSON.stringify(triStrip)); // <-- Bad Not working.
log.info("triStrip.length="+triStrip.length);
// Generating the actual Point strip.
var actualStrip = [];
for (var i = 0 ; i < triStrip.length; ++i)
{
actualStrip.push(pos[(triStrip[i]-1)*2+0]);
actualStrip.push(pos[(triStrip[i]-1)*2+1]);
}
log.info("actualStrip="+JSON.stringify(actualStrip));
log.info("actualStrip.length="+actualStrip.length);
Indices should be:
1, 5, 2, 6, 3, 7, 4, 8, 8, 5, 5, 9, 6, 10, 7, 11, 8, 12
I ended up re-creating the function to calculate the triangle strip indices. Have not fully tested it but it can re-create the 3x2 grid in the example from the website.
Here is the code:
// This calculates the triangle points in a rectangular triangle strip.
// Used for 3D Webgl texture mapping.
// Copyright Joshua Langley 2019.
var rows=2;
var cols=3;
var grid = rows*cols;
var offset;
var pos = [];
var index = 0;
var mpOffset = 1;
var offsetX, offsetY;
for (var row = 0; row <= rows; ++row)
{
offsetY = row * (mpOffset / rows);
for (var col = 0; col <= cols; ++col)
{
offsetX = col * (mpOffset / cols);
pos[index+0] = (offsetX);
pos[index+1] = (offsetY);
index+=2;
}
}
log.info("pos="+JSON.stringify(pos));
log.info("pos.length="+pos.length);
var rows=rows+1,cols=cols+1; // Important this counting Points not Squares.
var grid = rows*cols;
var offset;
var indices = [];
var indice = 0;
var offset;
var doublePoints = false;
var tPoint, bPoint;
for (var row = 0; row < rows; ++row)
{
for (var col = 0; col < (cols-1); ++col)
{
offset = row * rows + col;
tPoint = offset+1;
bPoint = offset+cols+1;
if (bPoint > grid)
continue;
indices.push(tPoint);
indices.push(bPoint);
if (offset > 0 && (bPoint+1) < grid && (offset+1) % cols == 0)
{
indices.push(bPoint);
indices.push(tPoint+1);
}
}
}
log.info("indices="+JSON.stringify(indices)); // Expected Result
log.info("indices.length="+indices.length);
var actualStrip = [];
for (var i = 0 ; i < indices.length; ++i)
{
actualStrip.push(pos[(indices[i]-1)*2+0]);
actualStrip.push(pos[(indices[i]-1)*2+1]);
}
log.info("actualStrip="+JSON.stringify(actualStrip));
log.info("actualStrip.length="+actualStrip.length);
I wrote a JavaScript function to find the number of odd integers, number of negative integers, average, and median value.
The code is accomplishing everything that I wanted it to, however, I am wondering if I can rewrite it to get the function to return an object rather than just the console log values. I've also included a link to my JS Bin (https://jsbin.com/digagozuru/edit?html,css,js,console,output)
Any advice or suggestions would be appreciated! Thanks.
var arrayAnalyzer = function(myArray) {
var odds = 0;
var negatives = 0;
var avg;
var median;
for (var i = 0; i < myArray.length; i++) {
if (myArray[i] % 2 !== 0) {
odds += 1;
}
if (myArray[i] < 0) {
negatives += 1;
}
}
console.log("There are " + odds + " odd numbers.");
console.log("There are " + negatives + " negative numbers.");
var sum = myArray.reduce(function(previousValue, currentValue) {
return previousValue + currentValue;
});
avg = sum / myArray.length;
console.log("The average is " + avg.toFixed(2));
var orderedArray = myArray.sort(function(a, b) {
return a - b;
});
if (orderedArray.length % 2 === 0) {
var position1 = orderedArray.length / 2;
var position2 = position1 - 1;
median = (orderedArray[position1] + orderedArray[position2]) / 2;
} else {
var position = Math.floor(orderedArray.length / 2);
median = orderedArray[position];
}
console.log("The median is " + median);
};
arrayAnalyzer([7, -3, 0, 12, 44, -5, 3]);
var arrayAnalyzer = function(myArray) {
var odds = 0;
var negatives = 0;
var avg;
var median;
for (var i = 0; i < myArray.length; i++) {
if (myArray[i] % 2 !== 0) {
odds += 1;
}
if (myArray[i] < 0) {
negatives += 1;
}
}
console.log("There are " + odds + " odd numbers.");
console.log("There are " + negatives + " negative numbers.");
var sum = myArray.reduce(function(previousValue, currentValue) {
return previousValue + currentValue;
});
avg = sum / myArray.length;
console.log("The average is " + avg.toFixed(2));
var orderedArray = myArray.sort(function(a, b) {
return a - b;
});
if (orderedArray.length % 2 === 0) {
var position1 = orderedArray.length / 2;
var position2 = position1 - 1;
median = (orderedArray[position1] + orderedArray[position2]) / 2;
} else {
var position = Math.floor(orderedArray.length / 2);
median = orderedArray[position];
}
console.log("The median is " + median);
// Returns an object with named attributes
return {
odds:odds,
negatives:negatives,
avg:avg,
median:median
};
};
var myArray = arrayAnalyzer([7, -3, 0, 12, 44, -5, 3]);
console.log("Odds: " + myArray.odds +
"\nNegatives: " + myArray.negatives +
"\nAverage:" + myArray.avg +
"\nMedian: " + myArray.median);
I'm not sure if I get your question right, but why dont create an object at the beginning of your method and then write the values in the object as soon as you calculated them?
var arrayAnalyzer = function(myArray) {
var odds = 0;
var negatives = 0;
var avg;
var median;
var result = {};
for (var i = 0; i < myArray.length; i++) {
if (myArray[i] % 2 !== 0) {
odds += 1;
}
if (myArray[i] < 0) {
negatives += 1;
}
}
result.negatives = negatives;
result.odds = odds;
var sum = myArray.reduce(function(previousValue, currentValue) {
return previousValue + currentValue;
});
avg = sum / myArray.length;
result.avg = avg;
var orderedArray = myArray.sort(function(a, b) {
return a - b;
});
if (orderedArray.length % 2 === 0) {
var position1 = orderedArray.length / 2;
var position2 = position1 - 1;
median = (orderedArray[position1] + orderedArray[position2]) / 2;
} else {
var position = Math.floor(orderedArray.length / 2);
median = orderedArray[position];
}
result.median = median;
return result;
};
console.log(arrayAnalyzer([7, -3, 0, 12, 44, -5, 3]));
I am using javascript function, where the loop should run 10 times, during these 10 times, it should throw a random question based on ,+ and - operations, in that too there should be 4 "+" questions, 3 "-" questions and 3 "" questions. And the loop should not run more than 10 times, someone please frame a logic for this...
My code so far:
<script type="text/javascript">
var op=new Array();
var addCount=0;
var subCount=0;
var mulCount=0;
var counter=0;
var no;
op[0]="+";
op[1]="-";
op[2]="x";
Array.prototype.chooseRandom = function()
{
return this[Math.floor(Math.random() * this.length)];
};
var a = [1, 2];
var b = [0, 2];
var c = [0, 1];
no=Math.floor((Math.random()*3));
while(addCount < 4|| subCount < 3 || mulCount < 3)
{
no=Math.floor((Math.random()*3));
if(no==0)
{
if(addCount<4)
{
addCount++;
var op1=Math.floor(Math.random() * (99 - 10+1)) + 10;
var op2=Math.floor(Math.random() * (99 - 10+1)) + 10;
}
else
{
no=a.chooseRandom();
}
}
else if(no==1)
{
if(subCount<3)
{
subCount++;
var no1=Math.floor(Math.random() * (99 - 10+1)) + 10;
var no2=Math.floor(Math.random() * (99 - 10+1)) + 10;
if(no1>no2)
{
var op1=no1;
var op2=no2;
}
else
{
var op1=no2;
var op2=no1;
}
}
else
{
no=b.chooseRandom();
}
}
else if(no==2)
{
if(mulCount<3)
{
mulCount++;
var op1=Math.floor(Math.random() * (99 - 10+1)) + 10;
var op2=Math.floor(Math.random() * (9 - 1+1)) + 1;
}
else
{
no=c.choseRandom();
}
}
counter++;
}
</script>
Make an array containing required number of +, - and * then random sort that aray using function below:
arr.sort(function() {return 0.5 - Math.random()})
Hoping it will help -
<script type="text/javascript">
function shuffle(o) {
for (var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
}
function getQuestions(){
var operators = shuffle(["+", "+", "+", "+", "-", "-", "-", "x", "x", "x"]);
var a = [1, 2, 3, 4, 5, 6];
var counter = 0;
Array.prototype.chooseRandom = function () {
return this[Math.floor(Math.random() * this.length)];
};
while (counter < 10) {
var op1 = a.chooseRandom();
var op2 = a.chooseRandom();
alert(op1 + operators[counter] + op2 + "?")
counter++;
}
}
getQuestions();
</script>
In my grid the column headers are named A,B,C...,AA,AB,AC,...etc like an excel spreadsheet. How can I convert the string to number like: A => 1, B => 2, AA => 27
Try:
var foo = function(val) {
var base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', i, j, result = 0;
for (i = 0, j = val.length - 1; i < val.length; i += 1, j -= 1) {
result += Math.pow(base.length, j) * (base.indexOf(val[i]) + 1);
}
return result;
};
console.log(['A', 'AA', 'AB', 'ZZ'].map(foo)); // [1, 27, 28, 702]
solution 1: best performance and browser compatibility
// convert A to 1, Z to 26, AA to 27
function lettersToNumber(letters){
var chrs = ' ABCDEFGHIJKLMNOPQRSTUVWXYZ', mode = chrs.length - 1, number = 0;
for(var p = 0; p < letters.length; p++){
number = number * mode + chrs.indexOf(letters[p]);
}
return number;
}
solution 2: best performance and compatibility and shorter code (Recommended)
// convert A to 1, Z to 26, AA to 27
function lettersToNumber(letters){
for(var p = 0, n = 0; p < letters.length; p++){
n = letters[p].charCodeAt() - 64 + n * 26;
}
return n;
}
solution 3: short code (es6 arrow function)
// convert A to 1, Z to 26, AA to 27
function lettersToNumber(letters){
return letters.split('').reduce((r, a) => r * 26 + parseInt(a, 36) - 9, 0);
}
test:
['A', 'Z', 'AA', 'AB', 'ZZ','BKTXHSOGHKKE'].map(lettersToNumber);
// [1, 26, 27, 28, 702, 9007199254740991]
lettersToNumber('AAA'); //703
Here's a quick example of the code you should implement.
This will work with any given number of letters.
function letterToNumbers(string) {
string = string.toUpperCase();
var letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', sum = 0, i;
for (i = 0; i < string.length; i++) {
sum += Math.pow(letters.length, i) * (letters.indexOf(string.substr(((i + 1) * -1), 1)) + 1);
}
return sum;
}
i just wrote a junk yard f##$ snippet... need to be optimized.. :)
charToNum = function(alpha) {
var index = 0
for(var i = 0, j = 1; i < j; i++, j++) {
if(alpha == numToChar(i)) {
index = i;
j = i;
}
}
console.log(index);
}
numToChar = function(number) {
var numeric = (number - 1) % 26;
var letter = chr(65 + numeric);
var number2 = parseInt((number - 1) / 26);
if (number2 > 0) {
return numToChar(number2) + letter;
} else {
return letter;
}
}
chr = function (codePt) {
if (codePt > 0xFFFF) {
codePt -= 0x10000;
return String.fromCharCode(0xD800 + (codePt >> 10), 0xDC00 + (codePt & 0x3FF));
}
return String.fromCharCode(codePt);
}
charToNum('A') => returns 1 and charToNum('AA') => returns 27;
// Given Column to Number
function colToNumber(str) {
let num = 0
let i = 0
while (i < str.length) {
num = str[i].charCodeAt(0) - 64 + num * 26;
i++;
}
return num;
}
//Given Number to Column name
function numberToCol(num) {
let str = '', q, r;
while (num > 0) {
q = (num-1) / 26;
r = (num-1) % 26
num = Math.floor(q)
str = String.fromCharCode(65 + r) + str;
}
return str;
}
I rewrote Yoshi's answer in a more verbose form that explains better how it works and is easier to port to other languages:
var foo = function(val) {
var base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var baseNumber = base.length;
var runningTotal = 0;
var characterIndex = 0;
var indexExponent = val.length - 1;
while( characterIndex < val.length ){
var digit = val[characterIndex];
var digitValue = base.indexOf(digit) + 1;
runningTotal += Math.pow(baseNumber, indexExponent) * digitValue;
characterIndex += 1
indexExponent -= 1
}
return runningTotal;
};
console.log(['A', 'AA', 'AB', 'ZZ'].map(foo)); // [1, 27, 28, 702]
Public Function ColLet2Num(Letras As String)
'RALONSO MAYO 2017
'A-> 1
'OQ ->407
'XFD->16384
Dim UnChar As String
Dim NAsc As Long
Dim F As Long
Dim Acum As Long
Dim Indice As Long
Letras = UCase(Letras)
Acum = 0
Indice = 0
For F = Len(Letras) - 1 To 0 Step -1
UnChar = Mid(Letras, F + 1, 1)
NAsc = Asc(UnChar) - 64
Acum = Acum + (NAsc * (26 ^ Indice))
Indice = Indice + 1
Next
If Acum > 16384 Then
MsgBox "La celda máxima es la XFD->16384", vbCritical
End If
ColLet2Num = Acum
End Function
const getColumnName = (columnNumber) => {
let columnName = "";
const alphabets = "abcdefghijklmnopqrstuvwxyz".toUpperCase();
while (columnNumber > 0) {
const rem = columnNumber % 26;
if (rem === 0) {
columnName += "Z";
columnNumber = columnNumber / 26 - 1;
} else {
columnName += alphabets[rem - 1];
columnNumber = Math.floor(columnNumber / 26);
}
}
return columnName.split("").reverse().join("");
};
console.log(getColumnName(27));
A good readability and performance example:
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
// create dict to O(1) access to letter number
const dict = Object.fromEntries(letters.split('').map((it, index) => [it, index + 1]));
function toNumber(col) {
return col
.toUpperCase()
.split('')
.reduce((acc, letter, index) => acc + Math.pow(letters.length, col.length - (index + 1)) * dict[letter], 0);
}
Highly inspired by the different solutions put forth on this page
//fixed the one taken from here
function colToNumber(str: string): number {
var num = 0
for (var i = 0; i < str.length; i++) {
const current_letter = str.charCodeAt(i) - 64
const current_char = str[i]
if (i == str.length - 1) {
num += current_letter
} else {
var current = current_letter * Math.pow(26, str.length - i - 1)
num += current
}
}
return num;
}
//Given Number to Column name (taken from here)
function numberToCol(num: number) {
var str = '', q: number, r: number;
while (num > 0) {
q = (num - 1) / 26;
r = (num - 1) % 26
num = Math.floor(q)
str = String.fromCharCode(65 + r) + str;
}
return str;
}
function test_both() {
const dic = new Map<number, string>()
dic.set(1,"A")
dic.set(10,"J")
dic.set(13,"M")
dic.set(33,"AG")
dic.set(63,"BK")
dic.set(66,"BN")
dic.set(206,"GX")
dic.set(502,"SH")
dic.set(1003,"ALO")
dic.set(100,"CV")
dic.set(10111,"NXW")
dic.set(10001,"NTQ")
dic.set(9002,"MHF")
dic.set(5002,"GJJ")
dic.set(3002,"DKL")
dic.set(16384,"XFD")
for (var key of dic.keys()) {
const expected_a1 = dic.get(key) || ""
//console.log(`${ key }, ${ expected_a1 } `)
var actual = numberToCol(key)
var actual_num = colToNumber(expected_a1)
if (actual.localeCompare(expected_a1) != 0) {
console.error(`key = ${key} == expected=${expected_a1} actual = ${actual} `)
}
if (actual_num != key) {
console.error(`expected = ${expected_a1} key = ${key} == actual = ${actual_num} `)
}
}
}