I need to convert some PHP to javascript in the client side.
Can someone point me the correct translation to this lines?
There's no problem if external libs are needed.
$a = pack("A32", "foobar");
$b = implode("", unpack("H32", ("foobar", "frigobar")));
You can find an implementation of PHP's pack function in JavaScript at locutus.io (it is fairly long so I haven't included it here).
PHP's implode function is equal to JavaScript's join:
implode(",", array("first", "second", "third")) // "first,second,third"
is the same as
["first", "second", "third"].join(",") // "first,second,third"
Locutus also created an unpack implementation but it was removed from his website. Here it is (although it was marked as "not production ready")
function unpack(format, data) {
// http://kevin.vanzonneveld.net
// + original by: Tim de Koning (http://www.kingsquare.nl)
// + parts by: Jonas Raoni Soares Silva - http://www.jsfromhell.com
// + parts by: Joshua Bell - http://cautionsingularityahead.blogspot.nl/
// +
// + bugfixed by: marcuswestin
// % note 1: Float decoding by: Jonas Raoni Soares Silva
// % note 2: Home: http://www.kingsquare.nl/blog/22-12-2009/13650536
// % note 3: Feedback: phpjs-unpack#kingsquare.nl
// % note 4: 'machine dependant byte order and size' aren't
// % note 5: applicable for JavaScript unpack works as on a 32bit,
// % note 6: little endian machine
// * example 1: unpack('d', "\u0000\u0000\u0000\u0000\u00008YĂ€");
// * returns 1: { "": -100.875 }
var formatPointer = 0, dataPointer = 0, result = {}, instruction = '',
quantifier = '', label = '', currentData = '', i = 0, j = 0,
word = '', fbits = 0, ebits = 0, dataByteLength = 0;
// Used by float decoding - by Joshua Bell
//http://cautionsingularityahead.blogspot.nl/2010/04/javascript-and-ieee754-redux.html
var fromIEEE754 = function(bytes, ebits, fbits) {
// Bytes to bits
var bits = [];
for (var i = bytes.length; i; i -= 1) {
var byte = bytes[i - 1];
for (var j = 8; j; j -= 1) {
bits.push(byte % 2 ? 1 : 0); byte = byte >> 1;
}
}
bits.reverse();
var str = bits.join('');
// Unpack sign, exponent, fraction
var bias = (1 << (ebits - 1)) - 1;
var s = parseInt(str.substring(0, 1), 2) ? -1 : 1;
var e = parseInt(str.substring(1, 1 + ebits), 2);
var f = parseInt(str.substring(1 + ebits), 2);
// Produce number
if (e === (1 << ebits) - 1) {
return f !== 0 ? NaN : s * Infinity;
}
else if (e > 0) {
return s * Math.pow(2, e - bias) * (1 + f / Math.pow(2, fbits));
}
else if (f !== 0) {
return s * Math.pow(2, -(bias-1)) * (f / Math.pow(2, fbits));
}
else {
return s * 0;
}
}
while (formatPointer < format.length) {
instruction = format.charAt(formatPointer);
// Start reading 'quantifier'
quantifier = '';
formatPointer++;
while ((formatPointer < format.length) &&
(format.charAt(formatPointer).match(/[\d\*]/) !== null)) {
quantifier += format.charAt(formatPointer);
formatPointer++;
}
if (quantifier === '') {
quantifier = '1';
}
// Start reading label
label = '';
while ((formatPointer < format.length) &&
(format.charAt(formatPointer) !== '/')) {
label += format.charAt(formatPointer);
formatPointer++;
}
if (format.charAt(formatPointer) === '/') {
formatPointer++;
}
// Process given instruction
switch (instruction) {
case 'a': // NUL-padded string
case 'A': // SPACE-padded string
if (quantifier === '*') {
quantifier = data.length - dataPointer;
} else {
quantifier = parseInt(quantifier, 10);
}
currentData = data.substr(dataPointer, quantifier);
dataPointer += quantifier;
if (instruction === 'a') {
currentResult = currentData.replace(/\0+$/, '');
} else {
currentResult = currentData.replace(/ +$/, '');
}
result[label] = currentResult;
break;
case 'h': // Hex string, low nibble first
case 'H': // Hex string, high nibble first
if (quantifier === '*') {
quantifier = data.length - dataPointer;
} else {
quantifier = parseInt(quantifier, 10);
}
currentData = data.substr(dataPointer, quantifier);
dataPointer += quantifier;
if (quantifier > currentData.length) {
throw new Error('Warning: unpack(): Type ' + instruction +
': not enough input, need ' + quantifier);
}
currentResult = '';
for (i = 0; i < currentData.length; i++) {
word = currentData.charCodeAt(i).toString(16);
if (instruction === 'h') {
word = word[1] + word[0];
}
currentResult += word;
}
result[label] = currentResult;
break;
case 'c': // signed char
case 'C': // unsigned c
if (quantifier === '*') {
quantifier = data.length - dataPointer;
} else {
quantifier = parseInt(quantifier, 10);
}
currentData = data.substr(dataPointer, quantifier);
dataPointer += quantifier;
for (i = 0; i < currentData.length; i++) {
currentResult = currentData.charCodeAt(i);
if ((instruction === 'c') && (currentResult >= 128)) {
currentResult -= 256;
}
result[label + (quantifier > 1 ?
(i + 1) :
'')] = currentResult;
}
break;
case 'S': // unsigned short (always 16 bit, machine byte order)
case 's': // signed short (always 16 bit, machine byte order)
case 'v': // unsigned short (always 16 bit, little endian byte order)
if (quantifier === '*') {
quantifier = (data.length - dataPointer) / 2;
} else {
quantifier = parseInt(quantifier, 10);
}
currentData = data.substr(dataPointer, quantifier * 2);
dataPointer += quantifier * 2;
for (i = 0; i < currentData.length; i += 2) {
// sum per word;
currentResult = ((currentData.charCodeAt(i + 1) & 0xFF) << 8) +
(currentData.charCodeAt(i) & 0xFF);
if ((instruction === 's') && (currentResult >= 32768)) {
currentResult -= 65536;
}
result[label + (quantifier > 1 ?
((i / 2) + 1) :
'')] = currentResult;
}
break;
case 'n': // unsigned short (always 16 bit, big endian byte order)
if (quantifier === '*') {
quantifier = (data.length - dataPointer) / 2;
} else {
quantifier = parseInt(quantifier, 10);
}
currentData = data.substr(dataPointer, quantifier * 2);
dataPointer += quantifier * 2;
for (i = 0; i < currentData.length; i += 2) {
// sum per word;
currentResult = ((currentData.charCodeAt(i) & 0xFF) << 8) +
(currentData.charCodeAt(i + 1) & 0xFF);
result[label + (quantifier > 1 ?
((i / 2) + 1) :
'')] = currentResult;
}
break;
case 'i': // signed integer (machine dependent size and byte order)
case 'I': // unsigned integer (machine dependent size & byte order)
case 'l': // signed long (always 32 bit, machine byte order)
case 'L': // unsigned long (always 32 bit, machine byte order)
case 'V': // unsigned long (always 32 bit, little endian byte order)
if (quantifier === '*') {
quantifier = (data.length - dataPointer) / 4;
} else {
quantifier = parseInt(quantifier, 10);
}
currentData = data.substr(dataPointer, quantifier * 4);
dataPointer += quantifier * 4;
for (i = 0; i < currentData.length; i += 4) {
currentResult =
((currentData.charCodeAt(i + 3) & 0xFF) << 24) +
((currentData.charCodeAt(i + 2) & 0xFF) << 16) +
((currentData.charCodeAt(i + 1) & 0xFF) << 8) +
((currentData.charCodeAt(i) & 0xFF));
result[label + (quantifier > 1 ?
((i / 4) + 1) :
'')] = currentResult;
}
break;
case 'N': // unsigned long (always 32 bit, little endian byte order)
if (quantifier === '*') {
quantifier = (data.length - dataPointer) / 4;
} else {
quantifier = parseInt(quantifier, 10);
}
currentData = data.substr(dataPointer, quantifier * 4);
dataPointer += quantifier * 4;
for (i = 0; i < currentData.length; i += 4) {
currentResult =
((currentData.charCodeAt(i) & 0xFF) << 24) +
((currentData.charCodeAt(i + 1) & 0xFF) << 16) +
((currentData.charCodeAt(i + 2) & 0xFF) << 8) +
((currentData.charCodeAt(i + 3) & 0xFF));
result[label + (quantifier > 1 ?
((i / 4) + 1) :
'')] = currentResult;
}
break;
case 'f': //float
case 'd': //double
ebits = 8;
fbits = (instruction === 'f') ? 23 : 52;
dataByteLength = 4;
if (instruction === 'd') {
ebits = 11;
dataByteLength = 8;
}
if (quantifier === '*') {
quantifier = (data.length - dataPointer) / dataByteLength;
} else {
quantifier = parseInt(quantifier, 10);
}
currentData = data.substr(dataPointer, quantifier * dataByteLength);
dataPointer += quantifier * dataByteLength;
for (i = 0; i < currentData.length; i += dataByteLength) {
data = currentData.substr(i, dataByteLength);
bytes = [];
for (j = data.length - 1; j >= 0; --j) {
bytes.push(data.charCodeAt(j));
}
result[label + (quantifier > 1 ?
((i / 4) + 1) :
'')] = fromIEEE754(bytes, ebits, fbits);
}
break;
case 'x': // NUL byte
case 'X': // Back up one byte
case '#': // NUL byte
if (quantifier === '*') {
quantifier = data.length - dataPointer;
} else {
quantifier = parseInt(quantifier, 10);
}
if (quantifier > 0) {
if (instruction === 'X') {
dataPointer -= quantifier;
} else {
if (instruction === 'x') {
dataPointer += quantifier;
} else {
dataPointer = quantifier;
}
}
}
break;
default:
throw new Error('Warning: unpack() Type ' + instruction +
': unknown format code');
}
}
return result;
}
So, to finish, your example would be written like so:
var a = pack("A32", "foobar");
var b = unpack("H32", ["foobar", "frigobar"]).join("");
Related
This question already has answers here:
Calculate string value in javascript, not using eval
(12 answers)
Closed 5 months ago.
I am trying to do a specific algorithm with Javascript, and... it's a little bit tricky. Here is what i am trying to do :
a console app (with nodejs and "readline"). I ask the user to make an operation, example :
"2 + 3 * 4 / 2 - 1"
what I want to do, is to read the string, turn it into an array, identify the priority operator (* and /), and then, replace (without regex) the operation at the place where previously there was the operation, and so on. With my example, it should do :
[2 + 12 / 2 - 1] => [2 + 6 - 1] => [8 - 1] => [7].
So far, i succeeded in making an app that can add with many operators, but I got 2 problems :
1/ I dont know how to make the priority
2/ my app does not support more than 3 operations...
Here is my code, I appreciate any helps to undrstand what I am doing wrong and any help to finally end this algo. Thank you :
const readline = require("readline-sync");
const operators = ["+", "-", "*", "/"];
const openParenthesis = ['(', '[', '{'];
const closeParenthesis = [')', ']', '}'];
var numericOperator = 0;
var operatorsList = []
var operatorArray = [];
var NumberA = 0;
var NumberB = 0;
var Total = 0;
var soloOperator = "";
var removeValFromIndex = [];
var indexOperator = [];
var opFound = "";
function askOperator() {
operator = readline.question(`make an operation: `)
operatorArray = operator.split(' ');
console.log(operatorArray, operatorArray.length)
}
askOperator();
splitArray(operatorArray);
function splitArray(sentenceArray) {
for (let i = 0; i < sentenceArray.length; i++) {
opFound = operators.find(el => el == sentenceArray[i]);
if(opFound == "*") {
const findMultiplyer = (element) => element == opFound;
indexOperator = sentenceArray.findIndex(findMultiplyer);
soloOperator = sentenceArray[indexOperator];
NumberA = sentenceArray[indexOperator - 1];
NumberB = sentenceArray[indexOperator + 1];
removeValFromIndex.push((indexOperator - 1), indexOperator, (indexOperator + 1));
for (var j = removeValFromIndex.length -1; j >= 0; j--){
sentenceArray.splice(removeValFromIndex[j],1);
}
} else if (opFound == "/") {
const findDivider = (element) => element == opFound;
indexOperator = sentenceArray.findIndex(findDivider);
soloOperator = sentenceArray[indexOperator];
NumberA = sentenceArray[indexOperator - 1];
NumberB = sentenceArray[indexOperator + 1];
removeValFromIndex.push((indexOperator - 1), indexOperator, (indexOperator + 1));
for (var j = removeValFromIndex.length -1; j >= 0; j--){
sentenceArray.splice(removeValFromIndex[j],1);
}
} else if (opFound == "+") {
const findAdd = (element) => element == opFound;
indexOperator = sentenceArray.findIndex(findAdd);
soloOperator = sentenceArray[indexOperator];
NumberA = sentenceArray[indexOperator - 1];
NumberB = sentenceArray[indexOperator + 1];
removeValFromIndex.push((indexOperator - 1), indexOperator, (indexOperator + 1));
for (var j = removeValFromIndex.length -1; j >= 0; j--){
sentenceArray.splice(removeValFromIndex[j],1);
}
} else if (opFound == "-") {
const findMinus = (element) => element == opFound;
indexOperator = sentenceArray.findIndex(findMinus);
soloOperator = sentenceArray[indexOperator];
NumberA = sentenceArray[indexOperator - 1];
NumberB = sentenceArray[indexOperator + 1];
removeValFromIndex.push((indexOperator - 1), indexOperator, (indexOperator + 1));
for (var j = removeValFromIndex.length -1; j >= 0; j--){
sentenceArray.splice(removeValFromIndex[j],1);
}
}
console.log("loop", opFound, "la", removeValFromIndex ,sentenceArray)
}
console.log("test", indexOperator, "other", soloOperator, NumberA, NumberB);
doMath(NumberA, NumberB)
}
function doMath(numA, numB) {
console.log("index in math", indexOperator)
switch (soloOperator) {
case '+' :
Total = (parseInt(numA) + parseInt(numB));
// operatorArray trouver * ou / si cest le cas on saute cette section
if (indexOperator > 1) {
operatorArray.splice((indexOperator), 0, Total.toString())
} else {
operatorArray.splice((indexOperator -1), 0, Total.toString())
}
if (operatorArray.length >= 3) {
return splitArray(operatorArray)
}
console.log("addition", Total, "new array", operatorArray );
break;
case '-' :
Total = numA - numB;
if (indexOperator > 1) {
operatorArray.splice((indexOperator), 0, Total.toString())
} else {
operatorArray.splice((indexOperator -1), 0, Total.toString())
}
if (operatorArray.length >= 3) {
return splitArray(operatorArray)
}
console.log("substract", Total, "new array", operatorArray);
break;
case '*' :
Total = numA * numB;
if (indexOperator > 1) {
operatorArray.splice((indexOperator), 0, Total.toString())
} else {
operatorArray.splice((indexOperator -1), 0, Total.toString())
}
if (operatorArray.length >= 3) {
return splitArray(operatorArray)
}
console.log(indexOperator,"multiply", Total, "new array", operatorArray);
break;
case '/' :
Total = numA / numB;
if (indexOperator > 1) {
operatorArray.splice((indexOperator), 0, Total.toString())
} else {
operatorArray.splice((indexOperator -1), 0, Total.toString())
}
if (operatorArray.length >= 3) {
return splitArray(operatorArray)
}
operatorArray.splice((indexOperator), 0, Total.toString())
console.log("divide", Total, "new array", operatorArray);
break;
default:
console.log("An error occured")
break;
}
}
so, I found a working solution, here it is, but still, there is a regex inside, is there any way to replace it by something else?
const readline = require("readline-sync");
var sentence = "";
function askOperator() {
sentence = readline.question(`make an operation: `);
}
askOperator();
function MathSolver() {
this.infixToPostfix = function (infix) {
var outputQueue = "";
var operatorStack = [];
var operators = {
"^": {
precedence: 4,
associativity: "Right"
},
"/": {
precedence: 3,
associativity: "Left"
},
"*": {
precedence: 3,
associativity: "Left"
},
// <<<< test
"%": {
precedence: 3,
associativity: "Left"
},
// >>>>
"+": {
precedence: 2,
associativity: "Left"
},
"-": {
precedence: 2,
associativity: "Left"
}
};
infix = infix.replace(/\s+/g, "");
infix = infix.split(/([\+\-\*\%\/\^\(\)])/).clean();
for (var i = 0; i < infix.length; i++) {
var token = infix[i];
console.log("token",token)
if (isNumeric(token)) {
outputQueue += token + " ";
} else if ("^*%/+-".indexOf(token) !== -1) {
var o1 = token;
var o2 = operatorStack[operatorStack.length - 1];
while (
"^*%/+-".indexOf(o2) !== -1 &&
((operators[o1].associativity === "Left" &&
operators[o1].precedence <= operators[o2].precedence) ||
(operators[o1].associativity === "Right" &&
operators[o1].precedence < operators[o2].precedence))
) {
outputQueue += operatorStack.pop() + " ";
o2 = operatorStack[operatorStack.length - 1];
}
operatorStack.push(o1);
} else if (token === "(") {
operatorStack.push(token);
} else if (token === ")") {
while (operatorStack[operatorStack.length - 1] !== "(") {
outputQueue += operatorStack.pop() + " ";
}
operatorStack.pop();
}
}
while (operatorStack.length > 0) {
outputQueue += operatorStack.pop() + " ";
}
return outputQueue;
};
}
let ms = new MathSolver();
console.log("ms>>", ms.infixToPostfix("10 - 15 * 20"));
const e = ms.infixToPostfix(sentence);
console.log("e>", e);
const s = [],
tokens = e.split(" ");
for (const t of tokens) {
const n = Number(t);
if (!isNaN(n)) {
s.push(n);
} else {
if (s.length < 2) {
throw new Error(`${t}: ${s}: insufficient operands.`);
}
const o2 = s.pop(),
o1 = s.pop();
switch (t) {
case "+":
s.push(o1 + o2);
break;
case "-":
s.push(o1 - o2);
break;
case "*":
s.push(o1 * o2);
break;
case "%":
s.push(o1, (o1 / 100) * o2);
break;
case "/":
s.push(o1 / o2);
break;
case "^":
s.push(Math.pow(o1, o2));
break;
default:
throw new Error(`Unrecognized operator: [${t}]`);
}
}
console.log(`${t}: ${s}`);
}
function isNumeric(num) {
return !isNaN(parseFloat(num)) && isFinite(num);
}
can anyone come with an idea of how to sort an integer without using an array, and without using string methods as well as sort() method?
for example
input: 642531
output: 123456
I started by writing 2 simple functions - one which checks the length of the number, the other one splits the integer at some point and switches between 2 desired numbers. Below are the 2 functions.
I got stuck with the rest of the solution...
function switchDigits(num, i) { // for input: num=642531, i = 4 returns 624135
let temp = num;
let rest = 0;
for (let j = 0; j < i - 1; j++) {
rest = rest * 10;
rest = rest + temp % 10;
temp = (temp - temp % 10) / 10;
}
let a = temp % 10;
temp = (temp - a) / 10;
let b = temp % 10;
temp = (temp - b) / 10;
temp = Math.pow(10, i - 2) * temp;
temp = temp + 10 * a + b;
temp = Math.pow(10, i - 1) * temp;
temp = temp + rest;
return temp;
}
function checkHowManyDigits(num) { //input: 642534, output: 6 (length of the integer)
let count = 0;
while (num > 0) {
let a = num % 10;
num = (num - a) / 10;
count++;
}
return count;
}
let num = 642534;
let i = checkHowManyDigits(num);
console.log(switchDigits(num));
It actually complicated requirement and so does this answer. It's pure logic and as it is it's a question from a test you should try understanding the logic on your own as a homework.
function checkHowManyDigits(num) { //input: 642534, output: 6 (length of the integer)
let count = 0;
while (num > 0) {
let a = num % 10;
num = (num - a) / 10;
count++;
}
return count;
}
function sortDigit(numOriginal) {
let i = checkHowManyDigits(numOriginal);
let minCount = 0;
let min = 10;
let num = numOriginal;
while (num > 0) {
let d = num % 10;
num = (num - d) / 10;
if (d < min) {
min = d;
minCount = 0;
} else if (d === min) {
minCount++;
}
}
let result = 0;
while (minCount >= 0) {
result += min * Math.pow(10, i - minCount - 1);
minCount--;
}
let newNum = 0;
num = numOriginal;
while (num > 0) {
let d = num % 10;
num = (num - d) / 10;
if (d !== min) {
newNum = newNum * 10 + d;
}
}
if (newNum == 0) return result;
else return result += sortDigit(newNum);
}
console.log(sortDigit(642531));
You could have a look to greater and smaller pairs, like
64
46
The delta is 18, which gets an idea if you compare other pairs, like
71
17
where the delta is 54. Basically any difference of two digits is a multiple of 9.
This in mind, you get a function for taking a single digit out of a number and a single loop who is sorting the digits by using the calculated delta and subtract the value, adjusted by the place.
function sort(number) {
const
getDigit = e => Math.floor(number / 10 ** e) % 10,
l = Math.ceil(Math.log10(number)) - 1;
let e = l;
while (e--) {
const
left = getDigit(e + 1),
right = getDigit(e);
if (left <= right) continue;
number += (right - left) * 9 * 10 ** e;
e = l;
}
return number;
}
console.log(sort(17)); // 17
console.log(sort(71)); // 17
console.log(sort(642531)); // 123456
console.log(sort(987123654)); // 123456789
So eventually I found the best solution.
*This solution is based on a Java solution I found in StackOverFlow forums.
let store = 0;
function getReducedNumbr(number, digit) {
console.log("Remove " + digit + " from " + number);
let newNumber = 0;
let repeateFlag = false;
while (number>0) {
let t = number % 10;
if (t !== digit) {
newNumber = (newNumber * 10) + t;
} else if (t == digit) {
if (repeateFlag) {
console.log(("Repeated min digit " + t + " found. Store is : " + store));
store = (store * 10) + t;
console.log("Repeated min digit " + t + " added to store. Updated store is : " + store);
} else {
repeateFlag = true;
}
}
number = Math.floor(number / 10);
}
console.log("Reduced number is : " + newNumber);
return newNumber;}
function sortNum(num) {
let number = num;
let original = number;
let digit;
while (number > 0) {
digit = number % 10;
console.log("Last digit is : " + digit + " of number : " + number);
temp = Math.floor(number/10);
while (temp > 0) {
console.log("subchunk is " + temp);
t = temp % 10;
if (t < digit) {
digit = t;
}
temp = Math.floor(temp/10);
}
console.log("Smallest digit in " + number + " is " + digit);
store = (store * 10) + digit;
console.log("store is : " + store);
number = getReducedNumbr(number, digit);
}
console.log(("Ascending order of " + original + " is " + store));
return store;
}
console.log(sortNum(4214173));
you can see how it works here https://jsfiddle.net/9dpm14fL/1/
I am using javascript to format the number with commas , it was working very fine.
But now the problem is if a value is comming in negative for example : -792004
It is returning the output like : -,792,004 that is comma is in the start.
How can I modify this method ?
Here is my code :
function Comma(number) {
number = '' + number;
if (number.length > 3) {
var mod = number.length % 3;
var output = (mod > 0 ? (number.substring(0, mod)) : '');
for (i = 0; i < Math.floor(number.length / 3); i++) {
if ((mod == 0) && (i == 0))
output += number.substring(mod + 3 * i, mod + 3 * i + 3);
else
output += ',' + number.substring(mod + 3 * i, mod + 3 * i + 3);
}
return (output);
} else return number;
}
The simplest way I know which will helps you is toLocaleString() method on number:
var x = 10033001;
var y = -10033001;
console.log(x.toLocaleString(), y.toLocaleString());
But for correction of your code, you can remove number sign with Math.abs and add it after with Math.sign.
var sign = Math.sign(number);
number = Math.abs(number);
// Do the conversion
return (sign < 0) ? ("-" + output) : output;
Try this:
const comma = function(number) {
const prefix = number < 0 ? '-' : ''
number = String(Math.abs(number))
if (number.length > 3) {
const mod = number.length % 3
let output = (mod > 0 ? (number.substring(0,mod)) : '')
for (let i = 0; i < Math.floor(number.length / 3); i++) {
if (mod === 0 && i === 0)
output += number.substring(mod+ 3 * i, mod + 3 * i + 3)
else
output+= ',' + number.substring(mod + 3 * i, mod + 3 * i + 3);
}
return prefix + output
} else {
return prefix + number
}
}
If the number is negative, it assigns - to prefix. Then it changes number to its absolute value (Math.abs(number)). In the end it returns value with prefix.
I'm trying to create a damerau-levenshtein distance function in JS.
I've found a description off the algorithm on WIkipedia, but they is no implementation off it. It says:
To devise a proper algorithm to calculate unrestricted
Damerau–Levenshtein distance note that there always exists an optimal
sequence of edit operations, where once-transposed letters are never
modified afterwards. Thus, we need to consider only two symmetric ways
of modifying a substring more than once: (1) transpose letters and
insert an arbitrary number of characters between them, or (2) delete a
sequence of characters and transpose letters that become adjacent
after deletion. The straightforward implementation of this idea gives
an algorithm of cubic complexity: O\left (M \cdot N \cdot \max(M, N)
\right ), where M and N are string lengths. Using the ideas of
Lowrance and Wagner,[7] this naive algorithm can be improved to be
O\left (M \cdot N \right) in the worst case. It is interesting that
the bitap algorithm can be modified to process transposition. See the
information retrieval section of[1] for an example of such an
adaptation.
https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance
The section [1] points to http://acl.ldc.upenn.edu/P/P00/P00-1037.pdf which is even more complicated to me.
If I understood this correctly, it's not that easy to create an implementation off it.
Here's the levenshtein implementation I currently use :
levenshtein=function (s1, s2) {
// discuss at: http://phpjs.org/functions/levenshtein/
// original by: Carlos R. L. Rodrigues (http://www.jsfromhell.com)
// bugfixed by: Onno Marsman
// revised by: Andrea Giammarchi (http://webreflection.blogspot.com)
// reimplemented by: Brett Zamir (http://brett-zamir.me)
// reimplemented by: Alexander M Beedie
// example 1: levenshtein('Kevin van Zonneveld', 'Kevin van Sommeveld');
// returns 1: 3
if (s1 == s2) {
return 0;
}
var s1_len = s1.length;
var s2_len = s2.length;
if (s1_len === 0) {
return s2_len;
}
if (s2_len === 0) {
return s1_len;
}
// BEGIN STATIC
var split = false;
try {
split = !('0')[0];
} catch (e) {
// Earlier IE may not support access by string index
split = true;
}
// END STATIC
if (split) {
s1 = s1.split('');
s2 = s2.split('');
}
var v0 = new Array(s1_len + 1);
var v1 = new Array(s1_len + 1);
var s1_idx = 0,
s2_idx = 0,
cost = 0;
for (s1_idx = 0; s1_idx < s1_len + 1; s1_idx++) {
v0[s1_idx] = s1_idx;
}
var char_s1 = '',
char_s2 = '';
for (s2_idx = 1; s2_idx <= s2_len; s2_idx++) {
v1[0] = s2_idx;
char_s2 = s2[s2_idx - 1];
for (s1_idx = 0; s1_idx < s1_len; s1_idx++) {
char_s1 = s1[s1_idx];
cost = (char_s1 == char_s2) ? 0 : 1;
var m_min = v0[s1_idx + 1] + 1;
var b = v1[s1_idx] + 1;
var c = v0[s1_idx] + cost;
if (b < m_min) {
m_min = b;
}
if (c < m_min) {
m_min = c;
}
v1[s1_idx + 1] = m_min;
}
var v_tmp = v0;
v0 = v1;
v1 = v_tmp;
}
return v0[s1_len];
}
What are your ideas for building such an algorithm and, if you think it would be too complicated, what could I do to make no difference between 'l' (L lowercase) and 'I' (i uppercase) for example.
The gist #doukremt gave: https://gist.github.com/doukremt/9473228
gives the following in Javascript.
You can change the weights of operations in the weighter object.
var levenshteinWeighted= function(seq1,seq2)
{
var len1=seq1.length;
var len2=seq2.length;
var i, j;
var dist;
var ic, dc, rc;
var last, old, column;
var weighter={
insert:function(c) { return 1.; },
delete:function(c) { return 0.5; },
replace:function(c, d) { return 0.3; }
};
/* don't swap the sequences, or this is gonna be painful */
if (len1 == 0 || len2 == 0) {
dist = 0;
while (len1)
dist += weighter.delete(seq1[--len1]);
while (len2)
dist += weighter.insert(seq2[--len2]);
return dist;
}
column = []; // malloc((len2 + 1) * sizeof(double));
//if (!column) return -1;
column[0] = 0;
for (j = 1; j <= len2; ++j)
column[j] = column[j - 1] + weighter.insert(seq2[j - 1]);
for (i = 1; i <= len1; ++i) {
last = column[0]; /* m[i-1][0] */
column[0] += weighter.delete(seq1[i - 1]); /* m[i][0] */
for (j = 1; j <= len2; ++j) {
old = column[j];
if (seq1[i - 1] == seq2[j - 1]) {
column[j] = last; /* m[i-1][j-1] */
} else {
ic = column[j - 1] + weighter.insert(seq2[j - 1]); /* m[i][j-1] */
dc = column[j] + weighter.delete(seq1[i - 1]); /* m[i-1][j] */
rc = last + weighter.replace(seq1[i - 1], seq2[j - 1]); /* m[i-1][j-1] */
column[j] = ic < dc ? ic : (dc < rc ? dc : rc);
}
last = old;
}
}
dist = column[len2];
return dist;
}
Stolen from here, with formatting and some examples on how to use it:
function DamerauLevenshtein(prices, damerau) {
//'prices' customisation of the edit costs by passing an object with optional 'insert', 'remove', 'substitute', and
//'transpose' keys, corresponding to either a constant number, or a function that returns the cost. The default cost
//for each operation is 1. The price functions take relevant character(s) as arguments, should return numbers, and
//have the following form:
//
//insert: function (inserted) { return NUMBER; }
//
//remove: function (removed) { return NUMBER; }
//
//substitute: function (from, to) { return NUMBER; }
//
//transpose: function (backward, forward) { return NUMBER; }
//
//The damerau flag allows us to turn off transposition and only do plain Levenshtein distance.
if (damerau !== false) {
damerau = true;
}
if (!prices) {
prices = {};
}
let insert, remove, substitute, transpose;
switch (typeof prices.insert) {
case 'function':
insert = prices.insert;
break;
case 'number':
insert = function (c) {
return prices.insert;
};
break;
default:
insert = function (c) {
return 1;
};
break;
}
switch (typeof prices.remove) {
case 'function':
remove = prices.remove;
break;
case 'number':
remove = function (c) {
return prices.remove;
};
break;
default:
remove = function (c) {
return 1;
};
break;
}
switch (typeof prices.substitute) {
case 'function':
substitute = prices.substitute;
break;
case 'number':
substitute = function (from, to) {
return prices.substitute;
};
break;
default:
substitute = function (from, to) {
return 1;
};
break;
}
switch (typeof prices.transpose) {
case 'function':
transpose = prices.transpose;
break;
case 'number':
transpose = function (backward, forward) {
return prices.transpose;
};
break;
default:
transpose = function (backward, forward) {
return 1;
};
break;
}
function distance(down, across) {
//http://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance
let ds = [];
if (down === across) {
return 0;
} else {
down = down.split('');
down.unshift(null);
across = across.split('');
across.unshift(null);
down.forEach(function (d, i) {
if (!ds[i]) {
ds[i] = [];
}
across.forEach(function (a, j) {
if (i === 0 && j === 0) {
ds[i][j] = 0;
} else if (i === 0) {
//Empty down (i == 0) -> across[1..j] by inserting
ds[i][j] = ds[i][j - 1] + insert(a);
} else if (j === 0) {
//Down -> empty across (j == 0) by deleting
ds[i][j] = ds[i - 1][j] + remove(d);
} else {
//Find the least costly operation that turns the prefix down[1..i] into the prefix across[1..j] using
//already calculated costs for getting to shorter matches.
ds[i][j] = Math.min(
//Cost of editing down[1..i-1] to across[1..j] plus cost of deleting
//down[i] to get to down[1..i-1].
ds[i - 1][j] + remove(d),
//Cost of editing down[1..i] to across[1..j-1] plus cost of inserting across[j] to get to across[1..j].
ds[i][j - 1] + insert(a),
//Cost of editing down[1..i-1] to across[1..j-1] plus cost of substituting down[i] (d) with across[j]
//(a) to get to across[1..j].
ds[i - 1][j - 1] + (d === a ? 0 : substitute(d, a))
);
//Can we match the last two letters of down with across by transposing them? Cost of getting from
//down[i-2] to across[j-2] plus cost of moving down[i-1] forward and down[i] backward to match
//across[j-1..j].
if (damerau && i > 1 && j > 1 && down[i - 1] === a && d === across[j - 1]) {
ds[i][j] = Math.min(ds[i][j], ds[i - 2][j - 2] + (d === a ? 0 : transpose(d, down[i - 1])));
}
}
});
});
return ds[down.length - 1][across.length - 1];
}
}
return distance;
}
//Returns a distance function to call.
let dl = DamerauLevenshtein();
console.log(dl('12345', '23451'));
console.log(dl('this is a test', 'this is not a test'));
console.log(dl('testing testing 123', 'test'));
What's the easiest way to convert a floating point number to its binary representation in Javascript? (e.g. 1.0 -> 0x3F800000).
I have tried to do it manually, and this works to some extent (with usual numbers), but it fails for very big or very small numbers (no range checking) and for special cases (NaN, infinity, etc.):
function floatToNumber(flt)
{
var sign = (flt < 0) ? 1 : 0;
flt = Math.abs(flt);
var exponent = Math.floor(Math.log(flt) / Math.LN2);
var mantissa = flt / Math.pow(2, exponent);
return (sign << 31) | ((exponent + 127) << 23) | ((mantissa * Math.pow(2, 23)) & 0x7FFFFF);
}
Am I reinventing the wheel?
EDIT: I've improved my version, now it handles special cases.
function assembleFloat(sign, exponent, mantissa)
{
return (sign << 31) | (exponent << 23) | (mantissa);
}
function floatToNumber(flt)
{
if (isNaN(flt)) // Special case: NaN
return assembleFloat(0, 0xFF, 0x1337); // Mantissa is nonzero for NaN
var sign = (flt < 0) ? 1 : 0;
flt = Math.abs(flt);
if (flt == 0.0) // Special case: +-0
return assembleFloat(sign, 0, 0);
var exponent = Math.floor(Math.log(flt) / Math.LN2);
if (exponent > 127 || exponent < -126) // Special case: +-Infinity (and huge numbers)
return assembleFloat(sign, 0xFF, 0); // Mantissa is zero for +-Infinity
var mantissa = flt / Math.pow(2, exponent);
return assembleFloat(sign, exponent + 127, (mantissa * Math.pow(2, 23)) & 0x7FFFFF);
}
I'm still not sure if this works 100% correctly, but it seems to work good enough.
(I'm still looking for existing implementations).
new technologies are making this easy and probably also more forward-compatible. I love extending built in prototypes, not everyone does. So feel free to modify following code to classical procedural approach:
(function() {
function NumberToArrayBuffer() {
// Create 1 entry long Float64 array
return [new Float64Array([this]).buffer];
}
function NumberFromArrayBuffer(buffer) {
// Off course, the buffer must be at least 8 bytes long, otherwise this is a parse error
return new Float64Array(buffer, 0, 1)[0];
}
if(Number.prototype.toArrayBuffer) {
console.warn("Overriding existing Number.prototype.toArrayBuffer - this can mean framework conflict, new WEB API conflict or double inclusion.");
}
Number.prototype.toArrayBuffer = NumberToArrayBuffer;
Number.prototype.fromArrayBuffer = NumberFromArrayBuffer;
// Hide this methods from for-in loops
Object.defineProperty(Number.prototype, "toArrayBuffer", {enumerable: false});
Object.defineProperty(Number.prototype, "fromArrayBuffer", {enumerable: false});
})();
Test:
(function() {
function NumberToArrayBuffer() {
// Create 1 entry long Float64 array
return new Float64Array([this.valueOf()]).buffer;
}
function NumberFromArrayBuffer(buffer) {
// Off course, the buffer must be ar least 8 bytes long, otherwise this is a parse error
return new Float64Array(buffer, 0, 1)[0];
}
if(Number.prototype.toArrayBuffer) {
console.warn("Overriding existing Number.prototype.toArrayBuffer - this can mean framework conflict, new WEB API conflict or double inclusion.");
}
Number.prototype.toArrayBuffer = NumberToArrayBuffer;
Number.fromArrayBuffer = NumberFromArrayBuffer;
// Hide this methods from for-in loops
Object.defineProperty(Number.prototype, "toArrayBuffer", {enumerable: false});
Object.defineProperty(Number, "fromArrayBuffer", {enumerable: false});
})();
var test_numbers = [0.00000001, 666666666666, NaN, Infinity, -Infinity,0,-0];
console.log("Conversion symethry test: ");
test_numbers.forEach(
function(num) {
console.log(" ", Number.fromArrayBuffer((num).toArrayBuffer()));
}
);
console.log("Individual bytes of a Number: ",new Uint8Array((666).toArrayBuffer(),0,8));
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>
Here's a function that works on everything I've tested it on, except it doesn't distinguish -0.0 and +0.0.
It's based on code from http://jsfromhell.com/classes/binary-parser, but it's specialized for 32-bit floats and returns an integer instead of a string. I also modified it to make it faster and (slightly) more readable.
// Based on code from Jonas Raoni Soares Silva
// http://jsfromhell.com/classes/binary-parser
function encodeFloat(number) {
var n = +number,
status = (n !== n) || n == -Infinity || n == +Infinity ? n : 0,
exp = 0,
len = 281, // 2 * 127 + 1 + 23 + 3,
bin = new Array(len),
signal = (n = status !== 0 ? 0 : n) < 0,
n = Math.abs(n),
intPart = Math.floor(n),
floatPart = n - intPart,
i, lastBit, rounded, j, exponent;
if (status !== 0) {
if (n !== n) {
return 0x7fc00000;
}
if (n === Infinity) {
return 0x7f800000;
}
if (n === -Infinity) {
return 0xff800000
}
}
i = len;
while (i) {
bin[--i] = 0;
}
i = 129;
while (intPart && i) {
bin[--i] = intPart % 2;
intPart = Math.floor(intPart / 2);
}
i = 128;
while (floatPart > 0 && i) {
(bin[++i] = ((floatPart *= 2) >= 1) - 0) && --floatPart;
}
i = -1;
while (++i < len && !bin[i]);
if (bin[(lastBit = 22 + (i = (exp = 128 - i) >= -126 && exp <= 127 ? i + 1 : 128 - (exp = -127))) + 1]) {
if (!(rounded = bin[lastBit])) {
j = lastBit + 2;
while (!rounded && j < len) {
rounded = bin[j++];
}
}
j = lastBit + 1;
while (rounded && --j >= 0) {
(bin[j] = !bin[j] - 0) && (rounded = 0);
}
}
i = i - 2 < 0 ? -1 : i - 3;
while(++i < len && !bin[i]);
(exp = 128 - i) >= -126 && exp <= 127 ? ++i : exp < -126 && (i = 255, exp = -127);
(intPart || status !== 0) && (exp = 128, i = 129, status == -Infinity ? signal = 1 : (status !== status) && (bin[i] = 1));
n = Math.abs(exp + 127);
exponent = 0;
j = 0;
while (j < 8) {
exponent += (n % 2) << j;
n >>= 1;
j++;
}
var mantissa = 0;
n = i + 23;
for (; i < n; i++) {
mantissa = (mantissa << 1) + bin[i];
}
return ((signal ? 0x80000000 : 0) + (exponent << 23) + mantissa) | 0;
}