How to compare two strings in this way? - javascript

I'm working with JavaScript, and I have two strings like this :
var week1="1.345.7", // each digit refers to one day of the week
week2="123..6.";
Now I want to return a value from 1 to 7 which refers to the number of days in common.
In the previous example I should return 2, because we have both weeks have Monday and Wednesday (1 and 3).
How can I achieve the above ?

Each character is either . or its index, so you can represent it with a bit.
"0b" + "1.345.7".replace(/./g, c=>c==='.'?0:1); // "0b1011101"
"0b" + "123..6.".replace(/./g, c=>c==='.'?0:1); // "0b1110010"
Then, you can use the bitwise operator AND &:
"0b1011101"
& "0b1110010";
// 0b1010000
Finally, you only need to convert it back to string and count the number of 1:
0b1010000.toString(2).split('1').length-1; // 2
Probably I wouldn't do it like this, but just for fun :)
In fact, to waste less memory, you could store the data as numbers instead of strings
0b1011101; // 93 - only needs 64 bits!
0b1110010; // 114 - only needs 64 bits!
And to retrieve the data
0b1011101 >> 6 & 1; // 1 - 1st bit
0b1011101 >> 5 & 1; // 0 - 2nd bit
0b1011101 >> 4 & 1; // 1 - 3rd bit
0b1011101 >> 3 & 1; // 1 - 4th bit
0b1011101 >> 2 & 1; // 1 - 5th bit
0b1011101 >> 1 & 1; // 0 - 6th bit
0b1011101 >> 0 & 1; // 1 - 7th bit

Get a list of digits from the first string using a quick regexp, then filter it to keep only the ones that are in the other one, then see how many there are with length.
(week1.match(/\d/g) || []) . filter(n => week2.includes(n)) . length
In a "code golf" spirit, you could write this as a generator, taking advantage of the ability of for...of to loop across characters in a string:
function *common(a, b) {
for (c of a) if (c !== '.' && b.includes(c)) yield c;
}
console.log(...common(a, b))

Just to throw another option out there, if you split a string with an empty string argument, you get an array of one-character strings. This makes them easy to iterate, but also if you're targeting browsers that support ECMAscript 5.1 (most notably IE 9+) you can use the reduce function. It's generally a good fit when you're passing in an array that you want to iterate and return a single value. This could be more concise, but I think it's easier to follow this way.
var week1="1.345.7";
var week2="123..6.";
function weekDaysInCommon(w1, w2) {
//split to convert w1 to an array.
//"1.345.7" becomes ["1", ".", "3", "4", "5", ".", "7"]
w1 = w1.split('');
//countCharactersAtSameIndex(w2) returns the function to use as the callback, with w2 accessible to it via closure
//the second arg, 0, is the initial value.
return w1.reduce(countCharactersAtSameIndex(w2), 0);
}
function countCharactersAtSameIndex(comparisonWeek) {
comparisonWeek = comparisonWeek.split('');
return function(total, day, index) {
if(comparisonWeek[index] === day) {
return total + 1;
} else {
return total;
}
}
}
document.write(weekDaysInCommon(week1, week2) + ' days in common');
Further reading:
MDN has a good doc on the reduce function - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

Related

Recursive Functions Runtime 2^n - 1

I have the following function fn(n).
function fn(n) {
if (n < 0) return 0;
if (n < 2) return n;
return fn(n - 1) + fn(n - 2);
}
I understand how this code works, but don't how to calculate time complexity for it.
Let’s do some examples:
For n = 3, you have 5 function calls. First fn(3), which in turn calls fn(2) and fn(1) and so on.
For n = 4, you have 9 function calls. First fn(4), which in turn calls fn(3) and fn(2) and so on.
Graphical representation of the 2 examples:
The leftmost nodes go down in descending order: fn(4), fn(3), fn(2), fn(1), which means that the height of the tree (or the number of levels) on the tree will be n.
The time complexity of this code is 2^n - 1. Although, if we count all calls will be just 9 calls for n = 4.
And the question is how we get 2^n - 1? I don't understand
One of the ways of calculating recursive algorithm's time complexity is using the Recursion Tree Method. For this particular case, we can write T(n)=T(n-1)+T(n-2)+2*O(1), where O(1) means constant time, since we have two comparison checks of n values using if. The recursion tree would look something like this:
1 n
2 (n-1) (n-2)
4 (n-2) (n-3) (n-3) (n-4)
8 (n-3)(n-4) (n-4)(n-5) (n-4)(n-5) (n-5)(n-6)
...
2^i for i-th level
Total work done will be the sum of all of the work done in each level, so we can write T(n)=1 + 2 + 4 + 8 + 16 + 32 + ... = 2^0 + 2^1 + 2^2 + 2^3 + 2^4 + ... + 2^i. This is a geometric series which means that the T(n) is equal to (1-2^n)/(1-2) = (1-2^n)/(-1) = 2^n - 1. Because of that the time complexity of the function fn(n) is O(2^n).
You could also approach this using Back Substitution method. We know the following:
T(0)=1
T(1)=1
T(2)=1
T(n)=T(n-1)+T(n-2)+2*O(n)≈2*T(n-1)
Substituting T(n-2) with T(n-1) is allowed. In doing so you will get a higher bound which is still true for T(n-1)+T(n-2).
After the substitution you can also write T(n-1)=2*T(n-2), T(n-2)=2*T(n-3), ...
Using these values and substituting them recursively in to the T(n) you will get:
T(n)=2*2*T(n-2)
=2*2*2*T(n-3)
=2*2*2*2*T(n-4)
=...
=2^i * T(n-i)
From there you can write n-i=1 => i=n-1 and substitute the i value in T(n). You can do this because T(1)=1 is one of the base conditions.
=> T(i)=2^(n-1) * T(n-(n-1))=2^(n-1) * T(n-n+1)=2^(n-1) * T(1)=2^(n-1)
This means that the time complexity is O(2^n).

JavaScript -- write a function that can solve a math expression (without eval)

Ultimately I want to take this:
2x + 3 = 5
and solve for x, by first subtract 3 from both sides so 2x = 2, then divide both sides by 2 so x = 1. I was thinking a lot how one should go about making a function like this in JavaScript that can return an array of the steps done in order, including the result. Obviously "eval" wouldn't do anything for this, so seemingly one has to re-create equations.
I initially thought to first of all, ignore X, and just try to make a function that can solve simple equations, without eval or any built-in function.
I figured that the first step is to break up the terms using .split, but I was having some trouble with this, as I need to split for multiple symbols. For example, say I have the simple expression to evaluate: 3 - 6 * 3 / 9 + 5. So before we even get into order of operations, just splitting up each term (and categorizing them) is the hard part, which is the main concrete-question I have at this point.
I started simply splitting one after the other, but I was having some problems, and especially considering the order.
function solve(eq) {
var minuses = eq.split("-"),
pluses = minuses.map(x=> x.split("+")),
timeses = pluses.map(x=>x.map(y=>y.split("*"))),
dividers = timeses.map(x=>x.map(y=>y.map(z=>z.split("/"))));
console.log(minuses, pluses, timeses, dividers);
}
solve("3 - 6 * 3 / 9 + 5");
As you can see, for each successive operator I need to map through each of he elements of the previous one to split it, and then I am left with an array of arrays etc...
So 1) how can I split up these terms more efficiently, without making a new variable for each one, and manually recursively mapping through each one? Seemingly I should just have some kind of dictionary of array keeping track of orders of operations (not considering parenthesis or exponents now): ["*","/","+","-"] -- and given that array, generate something similar to the last array in the above example ("dividers") which contains only constants, and somehow keep track of the which elements each of the stored arrays follows...
and 2) How can I solve the expression given the arrays of values?
I was just a little confused with the logic, I guess I need to work up from the last array and solve the constants one at a time, keeping track of which operator is the current one, but I'm not sure how exactly.
While your problem doesn't require to construct, binary expression tree is a good way to brainstorm the logic to solve a math query.
So for the query 3 - 6 * 3 / 9 + 5, the representative binary expression tree is:
plus
|_minus
| |_3
| |_divide
| |_times
| | |_3
| | |_6
| |_9
|_5
to solve above tree, you recursively solve from the leaf level up to the root.
Again, you don't need to construct a tree. It just helps us to see the logic of parsing here:
Get the last minus or plus expression in query and solve left and right child of that expression.
If no plus/minus, get the last times/division expression and solve left and right child
If meet a number, return that number value.
Given above logic, here is an implementation:
function solve(str) {
var expressionIndex = Math.max(str.lastIndexOf("-"), str.lastIndexOf("+"));
if (expressionIndex === -1) {
expressionIndex = Math.max(str.lastIndexOf("*"), str.lastIndexOf("/"));
}
if (expressionIndex === -1) {
var num = Number.parseInt(str.trim());
if (isNaN(num)) {
throw Exception("not a valid number");
} else {
return num;
}
} else {
var leftVal = solve(str.substring(0, expressionIndex).trim());
var rightVal = solve(str.substring(expressionIndex + 1).trim());
switch (str[expressionIndex]) {
case "+":
return leftVal + rightVal;
case "-":
return leftVal - rightVal;
case "*":
return leftVal * rightVal;
case "/":
return leftVal / rightVal;
}
}
}
function parse(str) {
var expressionIndex = Math.max(str.lastIndexOf("-"), str.lastIndexOf("+"));
if (expressionIndex === -1) {
expressionIndex = Math.max(str.lastIndexOf("*"), str.lastIndexOf("/"));
}
if (expressionIndex === -1) {
var num = Number.parseInt(str.trim());
if (isNaN(num)) {
throw Exception("not a valid number");
} else {
return { type: "number", value: num };
}
} else {
var leftNode = parse(str.substring(0, expressionIndex).trim());
var rightNode = parse(str.substring(expressionIndex + 1).trim());
return {
type: "expression",
value: str[expressionIndex],
left: leftNode,
right: rightNode
};
}
}
console.log(solve("3 - 6 * 3 / 9 + 5"));
console.log(parse("3 - 6 * 3 / 9 + 5"));
Above is a solution for very simple query with only +, -, *, / (no parenthesis, e.g.). For solving a equation like your first example requires a lot more of work.
EDIT: add a parse function to return the tree.
You can do that in following steps:
First of all use split() and split by the + and - which will occur after multiplication and division.
Then use map() on array and split() it again by * and /.
Now we have a function which will which will evaluate an array of numbers with operators to single number.
Pass the nested array to complete multiplication and division.
Then pass that result again to sovleSingle and perform addition and subtraction.
The function works same as eval as long as there are no brackets ().
Note: This doesnot matters the which occurs first among + and - or which occurs first among * and /. But *,/ should occur before +,-
function solveSingle(arr){
arr = arr.slice();
while(arr.length-1){
if(arr[1] === '*') arr[0] = arr[0] * arr[2]
if(arr[1] === '-') arr[0] = arr[0] - arr[2]
if(arr[1] === '+') arr[0] = +arr[0] + (+arr[2])
if(arr[1] === '/') arr[0] = arr[0] / arr[2]
arr.splice(1,1);
arr.splice(1,1);
}
return arr[0];
}
function solve(eq) {
let res = eq.split(/(\+|-)/g).map(x => x.trim().split(/(\*|\/)/g).map(a => a.trim()));
res = res.map(x => solveSingle(x)); //evaluating nested * and / operations.
return solveSingle(res) //at last evaluating + and -
}
console.log(solve("3 - 6 * 3 / 9 + 5")); //6
console.log(eval("3 - 6 * 3 / 9 + 5")) //6

How do I split multiline cells into arrays for Google Sheets?

I keep a spreadsheet of the titles and issue numbers I've read for various comics. I want to have a column that provides the count of the issues read for each title. Some titles have issue numbers in multiple lines. For example, the run of Avengers starting in 2016 has the following issue numbers listed in one cell, each range on a new line within the cell:
#1-11
#1.1-5.1
#1MU
#672-676
I tried to write a script that would separate each line into an array item, and then for each item in the array, extract the numbers using regular expressions to perform calculations to determine the total count. (The count of the above issues is 22, but the problem is getting the script to determine that for me and for the other titles as I update issue numbers.)
Here's what I have so far:
function calcIssueCount(x) {
// Set initial value to 0:
var issueCount = 0;
// Make an array from the lines of the cell
// split by the line break:
var box = x.split("\n");
for (var line in box) {
// Determine if the line includes a
// range of issue numbers, indicated
// by presence of a hyphen:
if ("-" in line === True) {
// Remove the # symbol from the string:
line = line.replace("#","");
// Extract the number before the hyphen
// and delete the hyphen:
var a = line(/[0-9}+\.|[0-9]+-/);
a = a.replace("-","");
// Extract the number after the hyphen
// and delete the hyphen:
var b = line(/-[0-9}+\.|[0-9]+/);
b = b.replace("-","");
// Turn a and b into numbers:
a = number(a)
b = number(b)
// Subtract the range start from the
// range end:
var c = b - a;
// Add 1 because a range of 1-5 includes 5
// issues, while 5-1 is only 4:
c += 1;
// Update the count for the number of
// issues in the cell by adding the
// line's value:
issueCount += c;
}
else {
// If there's no hyphen, there's no
// range; the count of issues on the
// line is 1:
issueCount += 1;
}
}
}
calcIssueCount(x) would have x as the cell name (e.g., D15).
I'm likely making mistakes all over the place, but I especially think I'm not understanding something about getting the cell data into the array into the first place. What am I missing here?
You want to retrieve "22" from the following value in a cell.
#1-11
#1.1-5.1
#1MU
#672-676
As the logic for retrieving "22" from the value, I understood as follows.
Retrieve 1 and 11 from #1-11.
Retrieve 1.1 and 5.1 from #1.1-5.1.
Retrieve 672 and 676 from #672-676.
Subtract before number from after number for each line.
Add 1 to all lines.
For the line without -, add only 1.
From above logic, (11 - 1 + 1) + (5.1 - 1.1 + 1) + (1) + (676 - 672 + 1) = 22 can be obtained. If my understanding of your logic is correct, how about the modification like below?
Modification points :
When for (var line in box) { is used, each element can be retrieved by box[line].
In this modicication, I used forEach.
In Javascript, the boolean express "true" and "false".
Line is not an object. So "-" in line occurs an error.
In this modification, I used indexOf().
In Javascript, number() is Number().
When you want to use calcIssueCount(x) as a custom function, in your current script, no values are returned. So if you want to retrieve issueCount, please add return issueCount.
I couldn't understand about the function of line(/[0-9}+\.|[0-9]+-/). I'm sorry for this.
In my modification, I retrieved both the before and after numbers using a regex of ([0-9.]+)-([0-9.]+). I think that there are several solutions for your situation. So please think of this as one of them.
Modified script :
Pattern 1 :
In this pattern, your script was modified.
function calcIssueCount(x) {
// Set initial value to 0:
var issueCount = 0;
// Make an array from the lines of the cell
// split by the line break:
var box = x.split("\n");
box.forEach(function(e){
if (e.indexOf("-") > -1) {
var numbers = e.match(/([0-9.]+)-([0-9.]+)/);
// Extract the number before the hyphen
// and delete the hyphen:
var a = Number(numbers[1]);
// Extract the number after the hyphen
// and delete the hyphen:
var b = Number(numbers[2]);
// Subtract the range start from the
// range end:
var c = b - a;
// Add 1 because a range of 1-5 includes 5
// issues, while 5-1 is only 4:
c += 1;
// Update the count for the number of
// issues in the cell by adding the
// line's value:
issueCount += c;
} else {
// If there's no hyphen, there's no
// range; the count of issues on the
// line is 1:
issueCount += 1;
}
});
return issueCount;
}
Pattern 2 :
This is other sample script. In this pattern, the regex is not used. The result is the same with pattern 1.
function calcIssueCount(x) {
return x.split("\n").reduce(function(c, e) {
if (e.indexOf("-") > -1) {
var numbers = e.slice(1).split("-");
c += (Number(numbers[1]) - Number(numbers[0])) + 1;
} else {
c += 1;
}
return c;
}, 0);
}
Result :
Note :
In this modified script, the regex of ([0-9.]+)-([0-9.]+) is used for the sample value. If you want to use values with other patterns, please tell me. At that time, can you show me more samples?
References :
Custom Functions in Google Sheets
Array.prototype.forEach()
String.prototype.indexOf()
Number
String.prototype.match()
If I misunderstand your question, I'm sorry.

Decomposing a value into results of powers of two

Is it possible to get the integers that, being results of powers of two, forms a value?
Example:
129 resolves [1, 128]
77 resolves [1, 4, 8, 64]
I already thought about using Math.log and doing also a foreach with a bitwise comparator. Is any other more beautiful solution?
The easiest way is to use a single bit value, starting with 1 and shift that bit 'left' until its value is greater than the value to check, comparing each bit step bitwise with the value. The bits that are set can be stored in an array.
function GetBits(value) {
var b = 1;
var res = [];
while (b <= value) {
if (b & value) res.push(b);
b <<= 1;
}
return res;
}
console.log(GetBits(129));
console.log(GetBits(77));
console.log(GetBits(255));
Since shifting the bit can be seen as a power of 2, you can push the current bit value directly into the result array.
Example
You can adapt solutions from other languages to javascript. In this SO question you'll find some ways of solving the problem using Java (you can choose the one you find more elegant).
decomposing a value into powers of two
I adapted one of those answers to javascript and come up with this code:
var powers = [], power = 0, n = 129;// Gives [1,128] as output.
while (n != 0) {
if ((n & 1) != 0) {
powers.push(1 << power);
}
++power;
n >>>= 1;
}
console.log(powers);
Fiddle
Find the largest power of two contained in the number.
Subtract from the original number and Add it to list.
Decrement the exponent and check if new 2's power is less than the number.
If less then subtract it from the original number and add it to list.
Otherwise go to step 3.
Exit when your number comes to 0.
I am thinking of creating a list of all power of 2 numbers <= your number, then use an addition- subtraction algorithm to find out the group of correct numbers.
For example number 77:
the group of factors is { 1,2,4,8,16,32,64} [ 64 is the greatest power of 2 less than or equal 77]
An algorithm that continuously subtract the greatest number less than or equal to your number from the group you just created, until you get zero.
77-64 = 13 ==> [64]
13-8 = 7 ==> [8]
7-4 = 3 ==> [4]
3-2 = 1 ==> [2]
1-1 = 0 ==> [1]
Hope you understand my algorithm, pardo my bad english.
function getBits(val, factor) {
factor = factor || 1;
if(val) {
return (val % 2 ? [factor] : []).concat(getBits(val>>1, factor*2))
}
return [];
}
alert(getBits(77));

How do I look up the particular bits or digits of a hex value in HTML5 / javascript?

I want to encode many things in hex. Here are examples.
var LAST_DIGITS = 0x000000A7; // Last 2 digits represent something
var MID_DIGITS = 0x00000E00; // 5th and 6th digits represent something else
Let's say I added LAST_DIGITS and MID_DIGITS together. That's 0x00000EA7 and represents the two different things I want to encode.
Is there some way I can just check a subset of that independently, in javascript/HTML5? Or do I have to turn it into a string or other collection and then reference indices explicitly?
In the above example, here's what I'm looking for
function getThemDigits (inHexValue)
{
// Check for 5th and 6th digits through my dream (not real!) function
inHexValue.fakeGetHexValueFunction(4,5); // returns 0E
// Check for last two digits for something else
inHexValue.fakeGetHexValueFunction(6,7); // returns A7
}
The common Bit-Operators (| & >> << etc.) are also available in JavaScript.
Lets assume that you always want two hexadecimal digits from the hexadecimal representation of that integer. And lets count the index of those digits from the right rather than from the left:
function GetHex(hex, offset) {
// Move the two hex-digits to the very right (1 hex = 4 bit)
var aligned = hex >> (4 * offset);
// Strip away the stuff that might still be to the left of the
// targeted bits:
var stripped = aligned & 0xFF;
// Transform the integer to a string (in hex representation)
var result = stripped.toString(16);
// Add an extra zero to ensure that the result will always be two chars long
if (result.length < 2) {
result = "0" + result;
}
// Return as uppercase, for cosmetic reasons
return result.toUpperCase();
}
Usage:
var LAST_DIGITS = 0x000000A7;
var MID_DIGITS = 0x00000E00;
var a = GetHex(LAST_DIGITS, 0);
var b = GetHex(MID_DIGITS, 2); // offset of 2 hex-digits, looking from the right

Categories