Finding 3 highest numbers in html data table and marking them red - javascript

Problem description:
I have data coming in that shows the electricity consumption of various buildings. These are in the form of html tables and may, and probably will, contain duplicates.
Using jQuery, how do I pick out the 3 highest numbers and mark them by making the background red?
Note: it's the three individually highest numbers that I want highlighted/marked red.
So if the in-data for example gives me 190, 180, 180, 170, 150 etc. I would like to highlight 190, 180 IN ONE PLACE/CELL, and 170.
Also, I wish to treat the tables as if I have only read-rights, not changing the table in any way except for the highlighting of the three highest numbers.

Psuedo code:
var high = [],
done = 0,
final = [];
$("td").each(function(){
high.push({ cell : $(this), val : parseInt($(this).text())});
});
// Now we got all cells sorted DESC
high.sort(function(a, b){
return b.val - a.val;
});
for(var i=0;i<high.length && done < 3;i++){
// Max highest 3
if( $.inArray(high[i].val, final) == -1 ) {
final.push(high[i].val);
done++;
}
high[i].cell.addClass("red");
}
Alternative psuedo code
var final = [],
done = 0,
items = $("td").sort(function(a, b){
return parseInt($(b).text()) - parseInt($(a).val());
});
items.each(function(){
var val = parseInt($(this).val());
if( $.inArray(val), final) == -1 ) {
done++;
final.push(val);
}
if( done == 3 ) {
return false; // Stop after we got enough
}
$(this).addClass("red");
});

Related

JavaScript check if array contains modified values, count unique pixels in an image

var image = new SimpleImage("lena.png");
var col = [];
var uniqcol = [];
for (var px of image.values()){
col.push([px.getRed,px.getGreen,px.getBlue]);
if(uniqcol.includes([px.getRed +- 1, px.getGreen +- 1, px.getBlue +- 1]) ){
print('not unique');
}else{
uniqcol.push([px.getRed,px.getGreen,px.getBlue]);
}
}
I would like to count the number of unique pixels within an image. A unique pixel being one which RGB values are not within 1 to anothers pixels. I have the above code but it does not work. I think the issue that I am having is with checking that the RGB values are either +1 or -1 from the selected pixel px value. If a unique pixel is found, id like to add to the the uniqcol array. Is there any other way to count the unique pixels, or a way to check that the RGB values are within 1 from the selected px value?
Thanks.
This tests each component to see if it's within 1 by subtracting the two, taking the absolute value, and checking if it's less than 2.
This is probably super inefficient. For each pixel you're iterating a potentially massive array until you get a match, or worst case, you don't find a match.
var image = new SimpleImage("lena.png");
var col = [];
var uniqcol = [];
for (var px of image.values()){
var found = uniqcol.find(function (el) {
return
Math.abs(el[0] - px.getRed) < 2 &&
Math.abs(el[1] - px.getGreen) < 2 &&
Math.abs(el[2] - px.getBlue) < 2;
});
if (!found) {
uniqcol.push([px.getRed,px.getGreen,px.getBlue]);
} else {
print('not unique');
}
}
Here's another approach that uses memoization. It should be a lot faster at the expense of storing a separate lookup structure.
Edit - I deleted this approach because it can fail. It's probably possible to do but quite tricky.
You need to check for all the different pixel values, putting +- will not match a range of values. .includes() looks for exact matches.
for (var px of image.values()) {
col.push([px.getRed,px.getGreen,px.getBlue]);
var found = false;
for (dRed of [-1, 0, +1]) {
for (dGreen of [-1, 0, +1]) {
for (dBlue of [-1, 0, +1]) {
if (uniqcol.includes([px.getRed + dRed, px.getGreen + dGreen, px.getBlue + dBlue]) {
found = true;
print("not unique");
break;
}
}
if (found) {
break;
}
if (found) {
break;
}
}
if (!found) {
uniqcol.push([px.getRed,px.getGreen,px.getBlue]);
}
}
This is probably not a very efficient way to do it, since it will search the entire image 9 times for each pixel. It would probably be better to loop through all the pixels, testing if all the colors are within a range of the current pixel:
if (px.getRed >= curPixel.getRed - 1 && px.getRed <= curPixel.getRed + 1 &&
px.getGreen >= curPixel.getGreen - 1 && px.getGreen <= curPixel.getGreen + 1 &&
px.getBlue >= curPixel.getBlue - 1 && px.getBlue <= curPixel.getBlue + 1)
A really efficient algorithm would involve sorting all the pixels (nested arrays of red, blue, and green values would be a good structure), then searching this. But that's more a topic for CodeReview.stackexchange.com.

Find the biggest number using javascript

I need to find the biggest number in a webpage (it is not my webpage).
Do I need javascript?
First I need to get the values from all the SPAN.f elements. Then, I must to compare the numbers into them to find out the biggest value.
How?
Let's say I've got 220 - 340 - 480 × 360 - 25/7/2012 from all the SPAN.f elements.
I need 480 to be highlighted, but I don't want /2012 to be highlighted because is part of the date. I mean, I want to highlight the 480, not the /2012.
I use GreaseMonkey with Firefox Nightly 22
THIS IS THE SCRIPT, but it does not work for me:
// ==UserScript==
// #name MAX NUMBER in page
// #include https://www.google.com/search*
// #version 1
// #grant none
// ==/UserScript==
var spans = document.querySelectorAll("SPAN.f");
var max = -Infinity;
var maxSpan;
Array.prototype.forEach.call(spans, function(span) {
var val = Number(span.textContent);
if (max < val)
{
max = val;
maxSpan = span;
}
});
if (maxSpan) maxSpan.style.backgroundColor = "yellow";
Try the code in the page into this link:
https://www.google.com/search?hl=es&tbs=sbi%3AAMhZZitcQmshs1XQVAv0-EH8Ix_18bqev3_1smHm0uRMYGzjaSYpDr6KHQ_1tWEwNyvZGPus4-VWnfO9P9071ZllBMIC3amGAveNcz_1uYWteP9OKZ1Si1Yz0urBuyIWQbBTQIue4Gntn5J8FIxaLI1kEHMdI2BIh6mrM6YGiMBT6DJyLlW4K-1kE0n_1d2fnXoNxKDd4jM034f9ctLfUYb4WvSaptVZknw_1jhHBFu9HeINrN15ha7k9Kzz1Ifm_1P5y7Vxws_1Qjr48P-rXcoqneksiRnfQyXwTGJeuMAD0wtgNnXubqYgzrPkxbZ-BonJ9Hgxvy5pv6lfEsGIuNzrtxd6QFdDKAP5keREmQ&ei=vTkuU87gBKay2wWU0IHoBg&ved=0CAgQiBw&biw=1024&bih=624&dpr=1
24/Dec/1789 is not a valid number, but should be a string, the others are numbers, and you can do
var biggest = Math.max(100, 220, 340);
If what you've got is a string, you could do
var str = '100 - 220 - 340 - 24/Dec/1789';
var biggest = Math.max.apply(null, str.split('-').filter(function(x) {
return !isNaN(x);
}));
FIDDLE;
I imagine you mean finding the largest number contained in SPAN elements having the class f (this is what the syntax SPAN.f stands for).
If so you don't need a regular expression and you don't even need to sort the values. Just iterate once over each value to find the smallest one.
Something like this:
var spans = document.querySelectorAll("SPAN.f");
var max = -Infinity;
var maxSpan;
Array.prototype.forEach.call(spans, function(span) {
var val = Number(span.textContent);
if (max < val)
{
max = val;
maxSpan = span;
}
});
if (maxSpan) maxSpan.style.backgroundColor = "yellow";
Fiddle: http://jsfiddle.net/C35W6/

Most efficient way to find range in array

I have a sorted array of integer numbers that can be positive of negative:
[ -30, -13, -10, -4, -1, 4, 23, 55, 90, 234, 433, 500 ]
I need to find the indexes of the lowest number that is greater or equal to zero and the greatest number that is lower or equal to 400.
What is the most efficient way to do it?
(I am using JavaScript but any language or pseudo code will be fine)
O(log N)
JSFiddle: http://jsfiddle.net/vCY68/
function binaryIndexOf(data, criteria) {
'use strict';
var minIndex = 0;
var maxIndex = data.length - 1;
var currentIndex;
var currentElement;
var result = null;
while (minIndex <= maxIndex) {
currentIndex = (minIndex + maxIndex) / 2 | 0;
currentElement = data[currentIndex];
var comparison = criteria(currentElement);
if (comparison[0] == 'right') {
minIndex = currentIndex + 1;
} else {
maxIndex = currentIndex - 1;
}
if (comparison[1]) {
result = currentIndex;
}
}
return result;
}
var firstPositive = binaryIndexOf(data, function(value) {
return value < 0 ? ['right', false] : ['left', true];
});
var lastLess400 = binaryIndexOf(data, function(value) {
return value > 400 ? ['left', false] : ['right', true];
});
Comparison function here returns 2 values: 1st is where to move after this comparison. 2nd is if the current value is acceptable.
PS: to save a time on implementing binary search the code from http://oli.me.uk/2013/06/08/searching-javascript-arrays-with-a-binary-search/ was taken, with minor modifications
PPS: potentially you can reduce number of comparisons if you initialize the search range manually and parameterize the second search with firstPositive + 1 start index
Do a binary search for 0 and for 400 in your array. If you hit 0 or 400 then that is the answer, otherwise check the array element that you reach as well as the elements to the left or right to see which one is the greatest smaller than 0 or largest less than 400. Complexity is O(log n) if your array is size n.
Do a binary search for the lowest element greater than 0 and for he highest element greater than 400 in your array.
You would need to do a slight modification in your search to check everytime if the element next to your index is still decreasing or increasing. For ex : if you reach the value 3 and the previous elemnt is -2, then 3 would be your answer(i.e instead of comparing for equality you are searching for the next highest or lowest value)
You will need 2 O(logn) operations to achieve this.

Javascript conditional find/replace

For my javascript project, I have a list that looks like this:
<li id="1">101.33, "book name 1"</li>
<li id="2">600.01, book name 2</li>
<li id="3">001.11, book name 3</li>
etc...
Of which I am supposed to do the following:
Remap the bullet list entries to a new (consistent) tag type (your choice – make it look pretty!).
For entries between 100 and 200, add 100 to the Dewey decimal number.
For entries between 400 and 500, add 200 to the Dewey decimal number.
Entries between 850 and 900 need to have 100 removed from the Dewey decimal number.
Entries between 600 and 650 need to have 17 added to the Dewey decimal number
For items that get changed, append “changed” to the record.
For items that do not get changed, append “no change” to the record.
For records that are incorrect, append “invalid record” to the record
But I'm not sure how to go about it. I want to target any number in the body, or within a list item. Right now I have this:
var z = document.body.li.innerHTML;
if (z >+ 100 && z <= 200)
{
var q = z + 100;
document.body.li.innerHTML=q;
}
}
Can anyone point me in the right direction of the best approach to do this in javascript? Should I be using find/replace instead?
EDIT: Attempted to amend the last ternary if else statement in David Thomas' code. Can't seem to get it to work:
//define valid record number as at-least-one-integer.at-least-one-integer
var reggie = /\d+(.)+d/
if (_newText = reggie) {
'Invalid Record';
}
else if (_newText === a[textProp]) {
'no change';
}
else(_newText != a[textProp]) {
'changed';
}
+ ')';
One approach, is the following (using plain JavaScript, albeit you'll need to use an up-to-date browser):
// first, we need to get all the 'li' items:
var lis = document.querySelectorAll('ul > li'),
// find the relevant text-property for this browser:
textProp = 'textContent' in document ? 'textContent' : 'innerText',
// empty string variable to allow assessment of changes:
_newText = '';
// Remap the bullet list entries to a new (consistent) tag type (your choice – make it look pretty!).
// do this one yourself.
// a function to zero-pad the numbers (I believe a requirement of Dewey Decimal):
function leftPadNumber(num, numLength, padChar) {
var nString = num.toString(),
major = parseInt(num, 10),
minor = parseFloat(nString.substring(nString.indexOf('.'))),
diff = numLength - major.toString().length;
if (diff > 0) {
return new Array(diff + 1).join(padChar || 0) + (major + minor);
} else {
return num;
}
}
// For entries between 100 and 200, add 100 to the Dewey decimal number.
// For entries between 400 and 500, add 200 to the Dewey decimal number.
// Entries between 850 and 900 need to have 100 removed from the Dewey decimal number.
// Entries between 600 and 650 need to have 17 added to the Dewey decimal number
// note that I've taken a very literal interpretation of 'between' (amend if necessary):
function amendedDeweyDecimal(num) {
if (num > 100 && num < 200) {
num += 100;
} else if (num > 400 && num < 500) {
num += 200;
} else if (num > 850 && num < 900) {
num -= 100;
} else if (num > 600 && num < 650) {
num += 17;
}
// happens if num falls somewhere outside of the above constraints:
return num;
}
// iterates over each element in the 'lis' nodeList/collection:
[].forEach.call(lis, function (a) {
/* replaces the found numbers ('m') in the string, using the two
functions, above, and assigns those to the _newText variable:
_newText = a[textProp].replace(/(\d{3}\.\d{2})/, function (m) {
return leftPadNumber(amendedDeweyDecimal(parseFloat(m)).toFixed(2), 3);
});
// For items that get changed, append “changed” to the record.
// For items that do not get changed, append “no change” to the record.
// returns the original text to the element, along with '(no change)'
// (if 'a[textProp]' is exactly equal to '_newText') or with '(changed)'
// (if the two variables are not identical):
a[textProp] = _newText + ' (' + (_newText === a[textProp] ? 'no change' : 'changed') + ')';
});
// For records that are incorrect, append “invalid record” to the record
// I have absolutely no idea how to assess an 'incorrect' record.
JS Fiddle demo.
References:
Array.prototype.forEach().
document.querySelectorAll().
Number.toFixed().
Number.toString().
String.parseFloat().
String.parseInt().
String.replace().
try jQuery .each
$('li').each(function(index, value) {
var val = $(this).text().split(','); //split into array
if (index >= 100 && index < 200) {
//do stuff
}
if (index >= 400 && index < 500) {
//do stuff
}
//etc
});
Regardless if you want to solve this using pure JavaScript or a helper library (like jQuery for example), i would suggest to disassemble your problem into smaller tasks and solve them one by one. At the end they will fit one into another and will build the complete solution. I would have started with three simple functions (reading your description they will be needed often):
the ability to list all LI elements separately
extract the number from the LI content
check if the number in in a given range
The code can look like this:
// count of all LI items
var elements = 0;
// fetch LI item one at a time
var element = document.getElementById(elements+1);
while (element != undefined) {
// get the number
var number = Number(getNumber(element.innerHTML));
// do something with number and or LI element
if (inRange(number, 100, 200)) { /* add 100 ...*/ } // and so on
// go to next element
elements++;
element = document.getElementById(elements+1);
}
function getNumber(elementContent) {
return elementContent.split(",")[0]; // TODO error handling
}
function inRange(number, min, max) {
return (number >= min) && (number <= max);
}
You can introduce simple objects and arrays to store information and states to track the changes of your content.

Better way for al lot of if else

I have a function to change the background color depending on the value of a slider
There are 35 different colors and I now use this code for it (of course it is longer)
if (value < 25) {
color = '#FFFFFF';
} else if (value > 25 && value < 50) {
color = '#F8F8F8';
} else if (value > 50 && value < 75) {
color = '#F0F0F0 ';
}
Is there a way to shorten this up?
If you're incrementing by 25, then make an Array of colors:
var colors = ['#FFFFFF', '#F8F8F8', '#F0F0F0 ', ... ]
And then do a little math to see which index to use.
color = colors[(value - (value % 25)) / 25];
Or if you prefer:
color = colors[Math.floor(value / 25)];
You could make it a two line statement, without arrays, by doing something similar to this:
var rgbvalue = 255-Math.floor(value/25);
var color = 'rgb('+rgbvalue+','+rgbvalue+','+rgbvalue+');';
Of course you would have to limit the value, so that the rgbvalue doesn't get smaller than 0, but I guess you can easily do that, if you know the possible values.
And if you want it to get dark faster, you can multiply the result of the Math.floor operation, like this:
var rgbvalue = 255-(Math.floor(value/25)*5);
And you have the advantage that you don't have to write a huge array of shades of gray.
More bullet-proof version (not fully -proof though)
var colors = ['#FFFFFF','#F8F8F8','#F0F0F0'];
/* this is not that necessary */
var value = input_value || default_input_value;
var color = colors[ Math.floor(value/25) ];
colors = {'#FFFFFF','#F8F8F8','#F0F0F0 '}
color=colors[(int)value/25];
You may need to adjust this depending on the range of value.
Ditch the && and cascade instead
if(values > 75){
//anything above 75 falls here
}
else if(value > 50){
//anything <= 75 but > 50 falls here
}
else if(values > 25){
//anything <= 50 but > 25 falls here
}
else {
//anything <= 25 falls here
}
You could use an array of objects that describe the color and the min and max of the range and then use a function to iterate through the array to find the color between the range.
function getColor(value) {
var colorRanges = [
{ color : '#FFFFFF', min : 0, max : 25 },
{ color : '#F8F8F8', min : 25, max : 50 },
{ color : '#F0F0F0', min : 50, max : 75 }
],
length = colorRanges.length;
while(length--) {
var colorRange = colorRanges[length];
if (value >= colorRange.min && value < colorRange.max) {
return colorRange.color;
}
}
// default color
return colorRanges[0].color;
}
With a little additional effort, you could expose a way to add new colors and ranges, have a default for the range interval, etc. If your colors and range interval are fixed however, this is probably overkill.

Categories