Even distribution of the replacement of words in a paragraph - javascript

Apologies if this is a dumb question - and seeing that the problem is mathematical in nature rather than a coding issue, I'm not sure how appropriate it is here, but I thought I'd ask.
My problem is as follows:
At the top of the page is a slider, which outputs a value from 0 to 100, depending on the slider's position.
Beneath this I have a paragraph of text that contains x number of pronouns (each wrapped in a <span class='pronoun'> tag), which I would like to replace with the person's name. In other words, I'm trying to replace every nth pronoun, the value of n being dependent on the position for the slider.
For example: (if my paragraph has 16 pronouns)
(Slider = 0) - no pronouns are replaced.
(slider = 25) - 4 pronouns are replaced, more or less equally distributed.
(slider = 50) - 8 pronouns are replaced, more or less equally distributed.
(slider = 75) - 12 pronouns are replaced, more or less equally distributed.
(slider = 100) - 16 pronouns are replaced.
Odd number of pronouns, or other positions of the slider on the scale might prove to be a little trickier, but rounding to the nearest integer should be accurate enough for what I am trying to achieve.
My current attempts are bringing me to this:
$('#pronouns').on('input', function() {
var slider = $(this).val();
$('#commentContainer').find('.sortable').each(function(index) {
var innerDivId = $(this).attr('id');
var name = atob($(this).data('all').name);
var comment = $(this).find('p');
var count = comment.children('.pronoun').length;
var i = 0;
var child = 0;
var children = [];
while (i <= count) {
n = parseInt(count/((count/100)*slider));
child = (n * i) + 1;
children.push(child);
comment.children('.pronoun:nth-child(' + child + ')').html(name);
i++;
}
console.log("slider is " + slider + ", pronouns : " + count + ", i is: " + i + ", n is: " + n + ", children are: " + children.toString());
});
});
However, as it stands my slider is having the opposite effect - the distribution is becoming larger, not smaller, as the slider goes to the right.
Any suggestions or pointers would be appreciated. There may also be an alternate solution I haven't remotely thought of, and I'm equally open to suggestions in that regard.

Related

How to add numbers in a for loop a set amount of times and display on HTML

**Hi, apologies for what some might seem as an easy question as I am quite new to Javascript coding. So here goes...on my html I have three input fields: Duct size, Insulation and Number of Holes. I want to show an array of measurements on the HTML in relation to the amount of holes, insulation and duct size entered. E.g first hole measurement, second hole, third hole etc... however the first measurement is calculated differently to the remaining measurements. Maybe this can clear up the formula and what I want to show on the screen:
1.Enter a measurement in duct size.
2.Enter insulation thickness.
3.Enter number of holes. With this information I want to calculate and display the measurements based off the number of holes and duct size measurement.
Here is an example:
Duct size: 400 mm
Insulation: 50 mm
Number of holes: 4
Insulation(50) * 2 - Duct size e.g 400 - 100 = 300 then 300 / 4 (number of holes) = 75 (want to show this number on HTML as: "Hole spacing = 75 mm ")
For first measurement(first Hole Spacing)=(spacing Of Holes)75 /2 + 50 (insulation) = 87.5 mm(This will be the first measurement to show on DOM).
Then for all other measurements relative (determined by number of holes >element)add spacing Of Holes to first Hole Spacing and display this.
E.g 87.5 + 75 = 162.5 (this is the second hole measurement displayed)
162.5 + 75 = 237.5 (this is the third hole measurement displayed)
237.5 + 75 = 312.5 (this the third hole measurement displayed)**
function ductPitotHoleCalculate() {
var ductSize = document.getElementById("duct-size").value;
var insulation = document.getElementById("insulation").value;
var amountOfHoles = document.getElementById("number-of-holes").value;
//It should multiply insulation by 2 and subtract from ductsize
var subtractInsulation = parseFloat(ductSize) - parseFloat(insulation) * 2;
//It should find the measurement between holes by dividing numberofHoles and subtractInsulation.
var spacingOfHoles = subtractInsulation / parseFloat(amountOfHoles);
//It should use the spacingOfHoles number and divide by 2 and add insulation for first hole and show on DOM
var firstHoleSpacing = spacingOfHoles / 2 + parseFloat(insulation);
// It should use a for loop to parse amountOfHoles and add spacingofHoles to firstHoleSpacing
var newAmount = parseFloat(amountOfHoles) - 2;
var myArray = [];
for (var i = 0; i < newAmount; i + spacingOfHoles) {
myArray.push(firstHoleSpacing + spacingOfHoles);
}
document.getElementById("answer-5").innerHTML = myArray;
}
<section id = "VSD-calculator">
<div id = "pitot-holes">
<h2> Calculate pitot hole duct measurements (type 0 for no insulation)
</h2>
<h2 id="answer-5"></h2>
<input type = "number" id = "duct-size" placeholder="Duct size in
mm"class="mytext">
<input type = "number" id = "insulation" placeholder="Insulation in
mm"class="mytext">
<input type = "number" id = "number-of-holes" placeholder="Number of
Holes"class="mytext">
<button onclick="ductPitotHoleCalculate()"id="calc-button" class = "calc"
>Calculate</button>
</div>
</section>
Thanks to all who helped me, I have solved this now! I think by explaining it and typing it down helped me make sense of it and write the code. I ended up using a while loop instead.
function ductPitotHoleCalculate() {
var ductSize = document.getElementById("duct-size").value;
var insulation = document.getElementById("insulation").value;
var amountOfHoles = document.getElementById("number-of-holes").value;
//It should multiply insulation by 2 and subtract from ductsize
var subtractInsulation = parseFloat(ductSize) - parseFloat(insulation) * 2;
//It should find the measurement between holes by dividing numberofHoles and
subtractInsulation.
var spacingOfHoles = subtractInsulation / parseFloat(amountOfHoles);
//It should use the spacingOfHoles number and divide by 2 and add insulation for
first
hole and show on DOM
var firstHoleSpacing = spacingOfHoles.toFixed(2) / 2 + parseFloat(insulation);
var i = 0;
var strHoles = parseFloat(amountOfHoles);
var myArray = '';
while (i < strHoles) {
myArray += firstHoleSpacing + (i * spacingOfHoles) + ' mm, ' + '<br />';
i++;
}
document.getElementById("answer-5").innerHTML = `Hole spacing = ${spacingOfHoles}
mm
` + '<br />' + myArray;
}

Tree exercise with a loop in javascript

I want to create a tree with *. I will give a number every time in order to specify the tree's height.
It should look something like this if I give number 4 as height for example:
*
***
*****
*******
I would like the tree to appear using console.log
I have done that:
var size = 4;
document.write(
"<center>" + Array.apply(0, new Array(size)).map(function(_, i) {
return new Array((i + 1) * 2).join(" * ");
}).join("<br>") + "</center>"
);
but it doesn't work if I use console.log
You just need to count the spaces on the left per floor.
The deepest one starts at j=0. The floor above at j=1. And so forth.
Given a height h,
floor h-1->j=0
floor h-2->j=1
floor 0->j=h-1
Notice that if you start at floor 0, you get j=h-1, and remove a space at every subsequent floor.
You can thus trivially write
const h = 4;
console.log(Array(h).fill(0).map((_,i)=>{
return ' '.repeat(h-1-i)+'*'.repeat(i*2+1)
}).join('\n'))
I created one like this.
function drawTree(height) {
for ( let i = 0; i < height ; i++ ) {
let star = '*';
let space = ' ';
for ( let j = 1; j <= i; j++ ) {
star = star + '**';
}
let gap = space.repeat(height-i-1);
star = gap + star;
console.log(star);
}
}
let number = prompt('Give number for tree height');
drawTree(number);
To clearify my comment. You won't be able to show html in the console. So you need to forget about tags. As when you are printing the tree you should know how big (height) it's going to be. So you know how many spaces will preceed your star.
Number of spaces are given by the height in total - the current "level" from top to bottom - 1
and the number of stars is given by 2 times of the current "level" plus 1
let spaces = " ".repeat(height-i-1);
let stars = "*".repeat(i*2+1);
So just use a loop to go through all the levels of the tree from top to bottom and concatinating the spaces and stars
Here's an example on how that works (example also prints out to a textarea for preview purposes as well as in the console)
https://codepen.io/relief_melone/pen/zYYRZmj

Calculate Ratios in JS like Humble Bundle

Do you know the sliders that you have on humblebundle.com when selecting where you want the money to go? Well when you adjust any one ratio it will automatically adjust the rest.
So say you're paying $20 no matter what but you want to adjust your tip to HB from $2 to $5, the ratios that were on the other stuff should automatically lowered to match but I have no idea what I'm doing.
This is as close as I get mathematically:
var settip = 50;
var tip = 5;
var devs = 75;
var donation = 20;
tip = settip;
var newAvail = 100 - tip;
var rCalc = 100 - (devs + donation);
devs = ((devs + rCalc) * newAvail) * .01;
donation = ((donation + rCalc) * newAvail) * .01;
console.log("New Ratio Calculation: " + rCalc);
console.log("New available space: " + newAvail);
console.log(tip);
console.log(devs);
console.log(donation);
The console logs are just so I can try and put it together in my head where things are going wrong. The numbers are also whole numbers first: 50 instead of .5 because Javascript is not accurate and I don't want to do the fix code every time, I'd rather figure out how to make the code work first and then think about optimizing.
So if anyone could guide me on a method or where I am going wrong here, then that'd be great. Thanks.
Tip is tip to the bundle maker.
Devs is tip to the devs.
Donation is tip to the donation box.
Each number is the ratio. Settip is the new ratio, I should be able to change any one value and have it automatically change all others but I can't even figure out how to do the first part so I couldn't begin to try for the second part of making it actually functional.
I think this problem is not as easy as it might seem if you want to cover different edge cases. Here I assume that you distribute money so you need following properties:
Each amount must be whole integer in cents
Sum of all amounts must be equal to the total sum
The simplest way to deal with it in JS is to make all calculations using whole numbers (e.g. sum in cents instead of dollars) and format them in more human-readable way on UI. Still even with this simplification it requires some non-trivial code:
function updateRates(rates, newValue, index) {
var i, len = rates.length;
var sum = 0;
for (i = 0; i < len; i++)
sum += rates[i];
var oldValue = rates[index];
var newRest = sum - newValue;
var curRest = sum - rates[index];
rates[index] = newValue;
var remainders = new Array(len);
var fraction, value, subsum = 0;
for (i = 0; i < len; i++) {
if (i === index) continue;
// special case, all other sliders were at 0 - split value equally
if (curRest === 0) {
fraction = 1.0 / (len - 1)
}
else {
fraction = rates[i] / curRest
}
value = newRest * fraction;
rates[i] = Math.floor(value); // always round down and then distribute rest according to the Largest remainder method
subsum += rates[i];
remainders[i] = {
index: i,
value: value - rates[i]
};
}
// sort remainders and distribute rest (fractions) accordingly
remainders.sort(function (a, b) {
var av = a.value;
var bv = b.value;
if (av === bv)
return 0;
if (av < bv)
return 1;
else
return -1;
});
for (i = 0; subsum < newRest; i++) {
rates[remainders[i].index] += 1;
subsum += 1;
}
return rates;
}
Some non-trivial tests:
1. updateRates([85,10,5], 82, 0) => [82, 12, 6]
2. updateRates([85,10,5], 83, 0) => [83, 11, 6]
3. updateRates([85,10,5], 84, 0) => [84, 11, 5]
4. updateRates([100,0,0], 95, 0) => [95, 2, 3]
5. updateRates([4,3,3,1], 0, 0) => [0, 5, 5, 1]
Pay attention to the example #5. If one used some naive rounding, sum will not be preserved. Effectively you need to distribute +4 in proportion 3:3:1. It means you should add +12/7, +12/7 and +4/7. Since 12/7 = 1 5/7, according to standard mathematical rules all three should be rounded up resulting in +2, +2, +1 but we only got +4 cents to distribute. To fix this issue the largest remainder method is used to distribute fractional cents among categories. Simply speaking the idea is that we first distribute only whole number of cents (i.e. always round down), calculate how many cents are actually left and then distribute them one by one. The biggest possible drawback of this method is that some rates that started with equal values might have different values after update. On the other hand this can't be avoided as example #4 shows: you can't split 5 cents equally between two categories.
To restate what I think you want: the three variables tip, devs and donation should always sum to 100. When one variable is updated, the other two should be updated to compensate. The automatic updates should keep the same ratios to each other (for example, if donation is double devs, and tips is updated, then the updated donation value should still be double the devs value).
If I've got that right, then this should work for you:
var tips = 5;
var devs = 20;
var donation = 75;
var setTips = function(newValue) {
tips = newValue;
var sum = devs + donation;
var devShare = devs / sum; // the share devs gets between devs and donation
var donationShare = 1 - devShare; // or could calculate as donation / sum
devs = (100 - tips) * devShare; // the remaining times it's share ratio
donation = (100 - tips) * donationShare; // the remaining times it's share ratio
};
// test it out
setTips(50);
console.log(tips, devs, donation);

Rounding to Significant Figures - Missing Zeros

I'm currently producing a JavaScript driven mathematics package, which focuses on rounding to various significant figures (S.F.) but I've run into a problem that I'm struggling to solve.
More on this problem later, but first some background for you.
The program is designed to select a completely random number within a given range and then automatically work out that number's relevant significant figures; for example:
Random Number: 0.097027 S.Fs: 9, 7, 0, 2, 7
Here is a screenshot of what I have produced to give you a visual representation:
As you can see, once the user has selected their number, they are then given the opportunity to click on four separate 'SF' buttons to view their random number presented to 1, 2, 3 and 4 S.Fs respectively.
For each S.F (1-4) the random number is rounded down, rounded up and rounded off to X SF and a scale below gives the user a more visual presentation to show why the SF value has been chosen by the program.
I've already written the vast majority of the code for this and tested it and so far the numbers are coming out how I'm expecting them to. Well nearly...
In the example I've given (0.097027); as you can see on the image I've included, the data for 4 S.F is absolutely correct and outputted accurately.
When I click on to the 3 SF button, I'd expect to see the following:
Random Number: 0.097027 3 S.F Rounded Up/Down/Off: 0.0970
However, what I'm actually getting is:
Random Number: 0.097027 3 S.F Rounded Up/Down/Off: 0.097
The program hasn't displayed the additional zero. This is a perfect example of a number in my program ending in a zero and in this case the zero is really significant and must be displayed.
The data is usually correct but there appears to be an issue with outputting significant zeros at the right time. I've researched the toFixed(x) method and if I assign toFixed(4) I get the correct required output, but because my numbers are generated randomly each time, they can range from a length of 5 figures, e.g. 89.404 up to > 10, e.g. `0.000020615.
So it looks like the toFixed method needs to be flexible/dynamic, e.g. toFixed(n) with a function run beforehand to determine exactly how many trailing zeros are needed?
Here are some key excerpts from my current solution for your consideration:
function generateNum() {
do {
genNumber = Math.random() * Math.pow (10, randomRange(-5, 5));
//Round
genNumber = roundToNSF(genNumber, 5, 0);
// This number must contain >1 digit which is 1 to 9 inclusive otherwise we may have e.g. 100. Rounding 100
}
while (!countNonZero(genNumber) || genNumber < 1E-05 || genNumber == 0);
//Round
genNumber = roundToNSF(genNumber, 5, 0);
genNumber = String(genNumber);
genNumber = Number(genNumber);
}
//----------------------------------------------------------------------------
function randomRange(min, max) {
/**
* Returns a random integer between min (inclusive) and max (inclusive)
* Using Math.round() will give you a non-uniform distribution!
*/
return Math.floor(Math.random() * (max - min + 1)) + min;
}
//---------------------------------------------------------------------------
//Click SF3 Button to reveal the data
function showSF3() {
//Remove any CSS properties on the buttons from previous use
removeButtonCSS();
document.getElementById('SFRounded').style.display = "block";
document.getElementById('scale').style.display = "block";
document.getElementById("SF3").className = document.getElementById("SF3").className + "buttonClick"; // this removes the blue border class
//Clear text
deleteRounded();
deleteScale();
//Run calculation
calculateAnswer();
//alert(genNumber.toFixed(4));
for (i = 3; i < 4; i++)
{
//Add The new data
sfRoundedTextBlock = document.getElementById('SFRounded');
//alert(downArray[i].toFixed(4));
//Data output to HTML.
sfRoundedTextBlock.innerHTML = sfRoundedTextBlock.innerHTML + '<p><strong>Number: </strong></br>' + String(genNumber) +
'</br>' + '<strong>Rounded down to ' + i + ' SF:</br></strong>' + downArray[i] + '</br>' +
'<strong>Rounded up to ' + i + ' SF:</br></strong>' + upArray[i] + '</br><strong>Rounded off to ' + i + ' SF:</br></strong>'
+ roundedArray[i] + '</br>' + '(See the scale below for why we choose <strong>' + roundedArray[i] + '</strong> as the rounded off value.)</p>';
}
}
//----------------------------------------------------------------------
var roundedArray = [];
var upArray = [];
var downArray = [];
var temp;
function calculateAnswer() {
//Clear Arrays
roundedArray = [];
upArray = [];
downArray = [];
// Work out the answer:
for (i = 0; i < 4; i++) {
var nSF = i + 1;
// Round OFF ...
temp = roundToNSF(genNumber, nSF, 0);
// We actually have to do this twice ...
roundedArray[nSF] = roundToNSF(temp, nSF, 0);
// Round UP ...
upArray[nSF] = roundToNSF(genNumber, nSF, 1);
// Round DOWN ...
downArray[nSF] = roundToNSF(genNumber, nSF, -1);
// e.g. x = 0.0098 rounded to 1SF is 0.010 initially (take the log of 0.0098 and try it!).
};
};
//-------------------------------------------------------------------------
//Globals
var aNumber;
var digits;
var way;
function roundToNSF(aNumber, digits, way){
// Round a number to n significant figures (can use roundToNDP provided we know how many decimal places):
if (way == undefined) { way = 0; }; // default is round off
if (aNumber !=0) {
if (aNumber > 0)
{
z = log10(aNumber);
}
else
{
z = log10(-aNumber);
};
z = Math.floor(z);
var nDP = digits - z - 1; // Rounding to nDP decimal places is equivalent to rounding to digits significant figures ...
var roundedNumber = roundToNDP(aNumber, nDP, way);
}
else {
roundedNumber = aNumber; // Number is zero ...
};
return Number(roundedNumber);
};
//---------------------------------------------------------------------------------
Update:
I'm still continuing to try and find a solution for this problem and an approach I have recently taken is to convert my randomly generated number into a searchable string variable and then use the indexOf(".") command to find the position of the decimal point (dp).
Then I've searched through my number, starting from the position of the dp to find the first instance of a significant, non-zero number [1-9].
var genNumber = 0.097027;
var rString = String(genNumber);
var positionofDP = rString.indexOf(".");
var regexp = /[1-9]/;
var positionofNonZero = Number(rString.search(regexp, positionofDP)); // Output would be '5'
I have then been able to target my search further, to determine whether my first significant number has any 'problematic' zeros in the immediate digits after it.
If there are any, then I set a Boolean variable to 'true' and then in a separate function create further text strings of my rounded off/down/up numbers, so I can then physically choose to add a '0' on to the end of the existing numerical characters.
This approach does work for me in isolated cases, but with my random number length ranging from 5-12 digits long, it still isn't dealing with all scenarios.
Maybe I need to create a dynamic toFixed(i) function? Any ideas would be greatly welcomed.
Instead of playing with the fixed points on an Int, you could manage the string directly.
Here's a link to a little fiddle: http://jsfiddle.net/5rw5G/4/
This not intended to completely/accurately solve your problem, but might help you see another solution.
function getRoundedSFs(num, SFCount) {
// Match every "leading zeros" before and after the .
var matches = num.toString().match(/^-?(0+)\.(0*)/);
// starting with "0."
if (matches) {
var firstIndex = matches[0].length;
var prefix = matches[0];
sf = Number(num.toString().substring(firstIndex, firstIndex + SFCount + 1));
sf = Math.round(sf / 10);
sf = prefix + sf.toString();
return Number(sf).toFixed(matches[2].length+SFCount);
}
// starting with something else like -5.574487436097115
else {
matches = num.toString().match(/^(-?(\d+))\.(\d+)/);
var decimalShift = SFCount - matches[2].length;
var rounded = Math.round(num * Math.pow(10, decimalShift));
rounded /= Math.pow(10, decimalShift);
return rounded.toFixed(decimalShift);
}
}
I've gone away again and I think I have now finally managed solve my initial problem.
There was a degree of confusion on my part surrounding when to use toFixed and toPrecision. I had previously attempted to convert my rounded up, down and off numbers into strings and then subsequently search through each of these to find the decimal point (".") and then work out the amount of trailing numbers, in order to then generate the correct toFixed point.
However, this was very hit and miss, given that my random number could be up to 12 digits, so what I've now done is to properly utilise toPrecision instead. For each 'SF button' (1-4) I have used the corresponding toPrecision point, e.g for SF1:
sfRoundedTextBlock.innerHTML = sfRoundedTextBlock.innerHTML + '<p><strong>Number: </strong></br>' + String(genNumber) +
'</br>' + '<strong>Rounded down to ' + i + ' SF:</br></strong>' + downArray[i].toPrecision(1) + '</br>' +
'<strong>Rounded up to ' + i + ' SF:</br></strong>' + upArray[i].toPrecision(1) + '</br><strong>Rounded off to ' + i + ' SF:</br></strong>'
+ roundedArray[i].toPrecision(1) + '</br>' + '(See the scale below for why we choose <strong>' + roundedArray[i].toPrecision(1) + '</strong> as the rounded off value.)</p>';
//Add The new scale data (Rounded Down)
downTextBlock = document.getElementById('down');
document.getElementById("down").innerHTML = String(downArray[i].toPrecision(1));
//Add The new scale data (Rounded Up)
upTextBlock = document.getElementById('up');
document.getElementById("up").innerHTML = String(upArray[i].toPrecision(1));
This was now giving me accurate results on every occasion, but there was still one hurdle left to jump. Occasionally I would reach a random scenario where scientific notation would have to be included in my outputted answer, e.g. 21819 rounded down to 1 SF, would read out at 2e+4 instead of 20000.
To combat this I setup my up, down and rounded figures into searchable strings, and then looked through these to find any illegal/scientific characters [a-z]. If I found any, I executed a slightly different version of my output which made use of parseFloat, which stripped out the scientific notation and displayed the correct figures:
//Convert Up, Down and Rounded into Strings based on their precision
var upString = String(upArray[i].toPrecision(1));
var downString = String(downArray[i].toPrecision(1));
var roundedString = String(roundedArray[i].toPrecision(1));
//Set up a regexp to search for characters [a-z], i.e. non-numeric
var regexp = /[a-z]/g;
//Search the up, down and rounded strings for non-numeric characters
var upResult = upString.match(regexp);
var downResult = downString.match(regexp);
var roundedResult = roundedString.match(regexp);
//If any of these strings contain a letter (non-numeric) we need to add in parseFloat to strip away the scientific notation included.
var containsChar = false;
if (upResult != null || downResult != null || roundedResult != null)
{
containsChar = true;
//alert("There is SN included here");
}
//Add The new data
sfRoundedTextBlock = document.getElementById('SFRounded');
if (containsChar == true)
{
sfRoundedTextBlock.innerHTML = sfRoundedTextBlock.innerHTML + '<p><strong>Number: </strong></br>' + String(genNumber) +
'</br>' + '<strong>Rounded down to ' + i + ' SF:</br></strong>' + parseFloat(downArray[i].toPrecision(1)) + '</br>' +
'<strong>Rounded up to ' + i + ' SF:</br></strong>' + parseFloat(upArray[i].toPrecision(1)) + '</br><strong>Rounded off to ' + i + ' SF:</br></strong>'
+ parseFloat(roundedArray[i].toPrecision(1)) + '</br>' + '(See the scale below for why we choose <strong>' + parseFloat(roundedArray[i].toPrecision(1)) + '</strong> as the rounded off value.)</p>';
//Add The new scale data (Rounded Down)
downTextBlock = document.getElementById('down');
document.getElementById("down").innerHTML = String(parseFloat(downArray[i].toPrecision(1)));
//Add The new scale data (Rounded Up)
upTextBlock = document.getElementById('up');
document.getElementById("up").innerHTML = String(parseFloat(upArray[i].toPrecision(1)));
}
Having tested this extensively it seems to be working as hoped.

Use jquery to dynamically number table columns diagonally

Hi there fellow coders,
I'm looking to find a way to fill a pre-built dynamic blank table with numbering (and colouring if possible) like so:
As you can see the numbering is ascending order diagonally. I know there's probably some way to calculate the number based on the tables td index but can't quite figure out how to do that for every column diagonally. Any help would be appreciated.
Update: Ok back from my Holidays. Thanks to all you clever people for your replies. As I'm sure you've all had to experience the pain in the neck clients can be, I've been told the spec has changed(again). This being the case I've had to put the grid/matrix into a database and output using a pivot table. Every square has to be customizable color-wise.
Nothing is going to waste though I have learnt quite a few nifty new javascript/jquery tricks from your responses I didn't know about before, so thanks, and I'll be sure to pay it forward :)
Here's what I came up with in the end.
Given you said "colouring if possible" I'll provide an example solution that doesn't do colours quite the way you want (it does it in a way that I found easier to code and more attractive to look at) but which does handle all the numbering correctly for varying table sizes.
The function below assumes the table already exists; in this demo I've included code that generates a table to whatever size you specify and then calls the function below to do the numbering and colours.
function numberDiagonally(tableId) {
var rows = document.getElementById(tableId).rows,
numRows = rows.length,
numCols = rows[0].cells.length,
sq = numRows + numCols - 1,
d, x, y,
i = 1,
dc,
c = -1,
colors = ["green","yellow","orange","red"];
diagonalLoop:
for (d = 0; d < sq; d++) {
dc = "diagonal" + d;
for (y = d, x = 0; y >= 0; y--, x++) {
if (x === numCols)
continue diagonalLoop;
if (y < numRows)
$(rows[y].cells[x]).html(i++).addClass(dc);
}
}
for (d = 0; d < sq; d++)
$(".diagonal" + d).css("background-color", colors[c=(c+1)%colors.length]);
}
Demo: http://jsfiddle.net/7NZt3/2
The general idea I came up with was to imagine a square twice as big as whichever of the x and y dimensions is bigger and then use a loop to create diagonals from the left edge of that bounding square going up and to the right - i.e., in the order you want the numbers. EDIT: Why twice as big as longer side? Because that's the first thing that came into my head when I started coding it and it worked (note that the variable i that holds the numbers that get displayed is not incremented for the imaginary cells). Now that I've had time to think, I realise that my sq variable can be set precisely to one less than the number of rows plus the columns - a number that ends up rather smaller for non-square tables. Code above and fiddle updated accordingly.
Note that the background colours could be set directly in the first loop, but instead I opted to assign classes and set the loops for each class later. Seemed like a good idea at the time because it meant individual diagonals could be easily selected in jQuery with a single class selector.
Explaining exactly how the rest works is left as an exercise for the reader...
UPDATE - this version does the colouring more like you asked for: http://jsfiddle.net/7NZt3/1/ (in my opinion not as pretty, but each to his own).
This fiddle populates an existing table with numbers and colors. It is not limited to being a 5x5 table. I didn't understand the logic of 15 being orange rather than yellow, so I simply grouped the diagonal cells into color regions.
// we're assuming the table exists
var $table = $('table'),
// cache the rows for quicker access
$rows = $table.find('tr'),
// determine number of rows
_rows = $rows.length,
// determine number of cells per row
_cols = $rows.first().children().length,
// determine total number of cells
max = _rows * _cols,
// current diagonal offset (for coloring)
d = 1,
// current row
r = 0,
// current cell
c = 0;
for (var i=1; i <= max; i++) {
// identify and fill the cell we're targeting
$rows.eq(r).children().eq(c)
.addClass('d' + d)
.text(i);
if (i < max/2) {
// in the first half we make a "line-break" by
// moving one row down and resetting to first cell
if (!r) {
r = c + 1;
c = 0;
d++;
continue;
}
} else {
// in the second half our "line-break" changes to
// moving to the last row and one cell to the right
if (c + 1 == _cols) {
c = 1 + r;
r = _rows -1;
d++;
continue;
}
}
r--;
c++;
}
Here's a jsFiddle that does what you asked for - http://jsfiddle.net/jaspermogg/MzNr8/8/
I took the liberty of making it a little bit user-customisable; it's interesting to see how long it takes the browser to render a 1000x1000 table using this method :-D
Assuming that each cell has an id of [column]x[row], here are teh codez for how to fill in the numbers of a square table of side length sidelength.
//populate the cells with numbers according to the spec
function nums(){
var xpos = 0
var ypos = 0
var cellval = 1
for(i=0;i<2*sidelength;i++){
if(i >= sidelength){
ypos = sidelength - 1
xpos = 1 + i - sidelength
$('td#' + xpos + 'x' + ypos).text(cellval)
cellval = cellval + 1
while(xpos + 1 < sidelength){
ypos = ypos-1
xpos = xpos+1
$('td#' + xpos + 'x' + ypos).text(cellval)
cellval = cellval + 1
}
} else {
ypos = i
xpos = 0
$('td#' + xpos + 'x' + ypos).text(cellval)
cellval = cellval + 1
while(!(ypos-1 < 0)){
ypos = ypos-1
xpos = xpos+1
$('td#' + xpos + 'x' + ypos).text(cellval)
cellval = cellval + 1
}
}
}
}
And here they are for how to colour the bugger.
// color the cells according to the spec
function cols(){
if(+$('td#0x0').text() === 99){
return false
} else {
$('td').each(function(index, element){
if(+$(this).text() > 22)
{
$(this).attr("bgcolor", "red")
}
if(+$(this).text() <= 22)
{
$(this).attr("bgcolor", "orange")
}
if(+$(this).text() <= 14)
{
$(this).attr("bgcolor", "yellow")
}
if(+$(this).text() <= 6)
{
$(this).attr("bgcolor", "green")
}
})
}
}
Enjoy, eh? :-)

Categories