Determine Document Order from Nodes - javascript

If I have two nodes in an HTML document, how can I tell which one comes first in HTML document order in Javascript using DOM methods?
For example,
function funstuff(a, b) {
//a and b can be any node in the DOM (text, element, etc)
if(b comes before a in document order) {
var t = b; b = a; a = t;
}
// process the nodes between a and b. I can handle this part
// when I know that a comes before b.
}

Resig to the rescue:
// Compare Position - MIT Licensed, John Resig
function comparePosition(a, b){
return a.compareDocumentPosition ?
a.compareDocumentPosition(b) :
a.contains ?
(a != b && a.contains(b) && 16) +
(a != b && b.contains(a) && 8) +
(a.sourceIndex >= 0 && b.sourceIndex >= 0 ?
(a.sourceIndex < b.sourceIndex && 4) +
(a.sourceIndex > b.sourceIndex && 2) :
1) +
0 :
0;
}

You can use the DOM function compareDocumentPosition which will return different numbers based on the two nodes' relationships:
DOCUMENT_POSITION_DISCONNECTED = 0x01;
DOCUMENT_POSITION_PRECEDING = 0x02;
DOCUMENT_POSITION_FOLLOWING = 0x04;
DOCUMENT_POSITION_CONTAINS = 0x08;
DOCUMENT_POSITION_CONTAINED_BY = 0x10;
Potentially the result could be the sum of more than one of these codes as the answer is a bitmask, but I can't imagine a situation where two of these conditions would be true at the same time. Also note that the "disconnected" result would be returned for instance with nodes that have been created but not added to the document tree yet

Rather difficult, I personally would itterate up each tree till I found a common ansester, then check which parent node(or the actual node if that low) comes first starting with firstChild and working through siblings, something like:
function OrderCheck(node1, node2){
var ar1 = [null, node1];
var ar2 = [null, node2];
for(var i = 1; ar1[i] != null; i++)
ar1[i+1]=ar1[i].parentNode;
for(var i = 1; ar2[i] != null; i++)
ar2[i+1]=ar2[i].parentNode;
ar1.reverse(); ar2.reverse(); // easier to work with.
i = 0;
while( ar1[i] === ar2[i] ){
if(ar1[i] === null)
return 0;
else
i++
}
if(ar1[i] === null)
return 2;
if(ar2[i] === null)
return 1;
if(i != 0){
var n = ar1[i-1].firstChild;
do{
if(n === ar1[i])
return 1;
if(n === ar2[i])
return 2;
}while(n = n.nextSibling);
}
return -1;// Shouldn't happen.
}
var order = OrderCheck(document.body, document.body.previousSibling);
if( order == 1){
// element 1 first
}else if(order == 2){
// element 2 first
}else{
// there was an error.
}
I did just edit this code in an attempt to fix two possible problems, I haven't tested this new edit however, so if something breaks I shall have to try again. (Edited again to fix a "doesn't even run" style bug).

Related

How to limit a number between several numbers (get the most nearest small number)? [duplicate]

Example: I have an array like this: [0,22,56,74,89] and I want to find the closest number downward to a different number. Let's say that the number is 72, and in this case, the closest number down in the array is 56, so we return that. If the number is 100, then it's bigger than the biggest number in the array, so we return the biggest number. If the number is 22, then it's an exact match, just return that. The given number can never go under 0, and the array is always sorted.
I did see this question but it returns the closest number to whichever is closer either upward or downward. I must have the closest one downward returned, no matter what.
How do I start? What logic should I use?
Preferably without too much looping, since my code is run every second, and it's CPU intensive enough already.
You can use a binary search for that value. Adapted from this answer:
function index(arr, compare) { // binary search, with custom compare function
var l = 0,
r = arr.length - 1;
while (l <= r) {
var m = l + ((r - l) >> 1);
var comp = compare(arr[m]);
if (comp < 0) // arr[m] comes before the element
l = m + 1;
else if (comp > 0) // arr[m] comes after the element
r = m - 1;
else // arr[m] equals the element
return m;
}
return l-1; // return the index of the next left item
// usually you would just return -1 in case nothing is found
}
var arr = [0,22,56,74,89];
var i=index(arr, function(x){return x-72;}); // compare against 72
console.log(arr[i]);
Btw: Here is a quick performance test (adapting the one from #Simon) which clearly shows the advantages of binary search.
var theArray = [0,22,56,74,89];
var goal = 56;
var closest = null;
$.each(theArray, function(){
if (this <= goal && (closest == null || (goal - this) < (goal - closest))) {
closest = this;
}
});
alert(closest);
jsFiddle http://jsfiddle.net/UCUJY/1/
Array.prototype.getClosestDown = function(find) {
function getMedian(low, high) {
return (low + ((high - low) >> 1));
}
var low = 0, high = this.length - 1, i;
while (low <= high) {
i = getMedian(low,high);
if (this[i] == find) {
return this[i];
}
if (this[i] > find) {
high = i - 1;
}
else {
low = i + 1;
}
}
return this[Math.max(0, low-1)];
}
alert([0,22,56,74,89].getClosestDown(75));
Here's a solution without jQuery for more effiency. Works if the array is always sorted, which can easily be covered anyway:
var test = 72,
arr = [0,56,22,89,74].sort(); // just sort it generally if not sure about input, not really time consuming
function getClosestDown(test, arr) {
var num = result = 0;
for(var i = 0; i < arr.length; i++) {
num = arr[i];
if(num <= test) { result = num; }
}
return result;
}
Logic: Start from the smallest number and just set result as long as the current number is smaller than or equal the testing unit.
Note: Just made a little performance test out of curiosity :). Trimmed my code down to the essential part without declaring a function.
Here's an ES6 version using reduce, which OP references. Inspired by this answer get closest number out of array
lookup array is always sorted so this works.
const nearestBelow = (input, lookup) => lookup.reduce((prev, curr) => input >= curr ? curr : prev);
const counts = [0,22,56,74,89];
const goal = 72;
nearestBelow(goal, counts); // result is 56.
Not as fast as binary search (by a long way) but better than both loop and jQuery grep https://jsperf.com/test-a-closest-number-function/7
As we know the array is sorted, I'd push everything that asserts as less than our given value into a temporary array then return a pop of that.
var getClosest = function (num, array) {
var temp = [],
count = 0,
length = a.length;
for (count; count < length; count += 1) {
if (a[count] <= num) {
temp.push(a[count]);
} else {
break;
}
}
return temp.pop();
}
getClosest(23, [0,22,56,74,89]);
Here is edited from #Simon.
it compare closest number before and after it.
var test = 24,
arr = [76,56,22,89,74].sort(); // just sort it generally if not sure about input, not really time consuming
function getClosest(test, arr) {
var num = result = 0;
var flag = 0;
for(var i = 0; i < arr.length; i++) {
num = arr[i];
if(num < test) {
result = num;
flag = 1;
}else if (num == test) {
result = num;
break;
}else if (flag == 1) {
if ((num - test) < (Math.abs(arr[i-1] - test))){
result = num;
}
break;
}else{
break;
}
}
return result;
}

Three Color Triangle

here is the question (https://www.codewars.com/kata/5a331ea7ee1aae8f24000175)
I've been searching for 2 days about this. I saw an essay (https://www.ijpam.eu/contents/2013-85-1/6/6.pdf). In codewars discussion, they say we can solve the question without using mat rules, if we design the code for complexity O(n).I did try that too but it doesnt work. I've tried my best but it didnt pass. Here is my code.
I did read this (Three colors triangles)
I wonder, is there any way to solve this without completely using Math ?
function triangle(row) {
let nextRow = []
let result = row.split("")
for(var j = 0; j < row.length-1; j++) {
nextRow= []
for(var i = 0; i < result.length - 1; i++) {
nextRow.push(nextColor(result[i],result[i+1]))
}
result = nextRow.slice(0)
}
return result.join("")
}
function nextColor(s1,s2) {
let colors = {"R": 1, "B": 2, "G": 3};
if(colors[s1] + colors[s2] == 6) return "G"
else if(colors[s1] + colors[s2] == 5) return "R"
else if(colors[s1] + colors[s2] == 4) return "B"
else if(colors[s1] + colors[s2] == 3) return "G"
else if(colors[s1] + colors[s2] == 2) return "R"
}
This solution solves it using < O(n2) with = O(n2) as a max. It uses two loops. One a while loop that ends so long as there is no new row to process. And an inner loop that iterates the colors of the current array.
This is not a solution to increase efficiency. It is a solution to show a non-efficient way with clarity on how the rules would work. Technically you could replace my use of "strings" with arrays, to increase efficiency, but that is not the point to illustrate on how an algorithm could work, albeit, inefficiently.
All other comments are in code comments:
function reduceTriangle( firstRow ) {
// from rules: You will be given the first row of the triangle as a string --> firstRow
let triangle = [];
// seed the triangle so we can loop it until the process is done
triangle.push( firstRow );
// lets loop this
let onRow = 0;
while (onRow < triangle.length) {
// lets determine the next generated row
// the rules given for adjacent colors are:
// Colour here: G G B G R G B R
// Becomes colour here: G R B G
// We'll also assume that order of the two-pattern doesn't matter: G B -> R, too! (from example)
let newRow = "";
for (let c = 0; c < triangle[onRow].length - 1; c++) {
let twoPattern = triangle[onRow].substring(c, c + 2); // GG, BG, etc.
// console.log(twoPattern);
let onePattern = false; // hmmm?
if (twoPattern == "RR" || twoPattern == "GG" || twoPattern == "BB") {
onePattern = twoPattern.substring(0, 1); // R || G || B
}
else {
if (twoPattern == "BG" || twoPattern == "GB") {
onePattern = "R"; // frome rules
}
else if (twoPattern == "RG" || twoPattern == "GR") {
onePattern = "B"; // frome rules
}
if (twoPattern == "BR" || twoPattern == "RB") {
onePattern = "G"; // frome rules
}
}
// hmmm cont'd:
if (onePattern !== false) {
newRow += onePattern;
}
}
if (newRow.length > 0) {
triangle.push( newRow.toString() ); // toString so we get a deep copy to append to triangle
}
// lets move to the next row, if none added, then the loop will end next cycle
onRow++;
}
return triangle;
}
console.log( reduceTriangle( "RRGBRGBB" ) );
console.log( reduceTriangle( "RBRGBRBGGRRRBGBBBGG" ) );

Levenshtein distance from index 0

I've been working through "The Algorithm Design Manual" section 8.2.1 Edit Distance by Recursion. In this section Skiena writes, "We can define a recursive algorithm using the observation that the last character in the string must either be matched, substituted, inserted, or deleted." That got me wondering, why the last character? This is true for any character based on the problem definition alone. The actual Levenshtein distance algorithm makes recursive calls from the back of the strings. Why? There's no reason you couldn't do the opposite, right? Is it just a simpler, more elegant syntax?
I'm flipping the algorithm around, so it iterates from the front of the string. My attempt is below. I know my implementation doesn't work completely (ex: minDistance("industry", "interest") returns 5 instead of 6). I've spent a couple hours trying to figure out what I'm doing wrong, but I'm not seeing it. Any help would be much appreciated.
var matchChar = (c,d) => c === d ? 0 : 1;
var minDistance = function(word1, word2) {
var stringCompare = function(s, t, i, j) {
if(i === s.length) return Math.max(t.length-s.length-1,0)
if(j === t.length) return Math.max(s.length-t.length-1,0)
if(cache[i][j] !== undefined) {
return cache[i][j]
}
let match = stringCompare(s,t,i+1,j+1) + matchChar(s[i], t[j]);
let insert = stringCompare(s,t,i,j+1) + 1;
let del = stringCompare(s,t,i+1,j) + 1;
let lowestCost = Math.min(match, insert, del)
cache[i][j] = lowestCost
return lowestCost
};
let s = word1.split('')
s.push(' ')
s = s.join('')
let t = word2.split('')
t.push(' ')
t = t.join('')
var cache = []
for(let i = 0; i < s.length; i++) {
cache.push([])
for(let j = 0; j < t.length; j++) {
cache[i].push(undefined)
}
}
return stringCompare(s, t, 0, 0)
}
The lines
if(i === s.length) return Math.max(t.length-s.length-1,0)
if(j === t.length) return Math.max(s.length-t.length-1,0)
look wrong to me. I think they should be
if(i === s.length) return t.length-j
if(j === t.length) return s.length-i

Dynamic condition in IF statement

I want to make condition of if statement dynamically in javascript,
check my code
var t = ['b','a']
if(t[0] !== 'a' && t[1] !== 'a'){console.log('remaining element')}
here t might be vary at any time say t = ['b','a','c'] then I need to write if condition like this
if(t[0] !== 'a' && t[1] !== 'a' && t[2] !== 'a'){console.log('remaining element')}
How can I rewirte this code efficiently?
You can use Array.prototype.every like this
if (t.every(function(currentElement) { return currentElement !== "a"; })) {
console.log('remaining element');
}
This works with arbitrary number of elements.
On older environments which do not support Array.prototype.every, you can use the plain for loop version
var flag = true;
for (var i = 0 ; i < t.length; i += 1) {
if (t[i] === "a") {
flag = false;
break;
}
}
if (flag) {
console.log('remaining element');
}

javascript value coming up empty not sure how to handle it

function updategeneral() {
//tmp = "fine_" + tmp + "_";
var actual = doc.findItem("1speed").value;
var posted = doc.findItem("2speed").value;
amt = "";
if (1speed != "" && 2speed != "") {
var a = 1 * 1speed;
var b = 1 * 2speed;
if (a - b <= 9) {
alert(amt);
amt = doc.getDefault("general_spb_1_to_15");
} else if (a - b <= 15) {
amt = doc.getDefault("general_spb_16_to_25");
} else if (a - b <= 25) {
amt = doc.getDefault("general_spb_15_to_19");
} else if (a - b <= 29) {
amt = doc.getDefault("general_spb_26+");
}
doc.findItem("mcare_amount").value = amt;
alert(doc.findItem("mcare_amount").value = amt);
}
}
Default values are:
general_spb_1_to_15=30.00 || general_spb_16_to_25=40.00 || general_spb_26+=50.00
My problem is when amt is empty or 0 it is always going to general_spb_1_to_15=30.00. I am not sure how to fix this- can someone please help? The values that I am using are 1speed = 20 and 2speed = 25 which is negative or empty.
Assuming your browser engine is interpreting 1speed and 2speed as variables (some will, some won't -- variable names aren't supposed to start with numbers so it would probably be wise to replace these with speed1 and speed2)...
But assuming that, then the case that you describe is seeing a value of a - b = -5 when processing your if statements, which means that it is being caught by
if (a - b <= 9) {
alert(amt);
amt = doc.getDefault("general_spb_1_to_15");
}
To change the result you are getting, you should add another option to the if statement structure. Perhaps:
if (b > a)
{
//do something for this special case
} else if (a - b <= 9) {
alert(amt);
amt = doc.getDefault("general_spb_1_to_15");
} else if ...
You may also want to specifically handle the case where one or both of speed1/speed2 are empty as an else on the outer if block.

Categories

Resources