Related
I'm learning a bit of JavaScript, but I'm having hard time understanding the lesson on FreeCodeCamp about the recursion countdown (link).
In the lesson, there this initial example. But I'm confused on how it operates:
function countup(n) {
if (n < 1) {
return [];
} else {
const countArray = countup(n - 1);
countArray.push(n);
return countArray;
}
}
console.log(countup(5));
I see the steps that this function performs (in order), as something like this:
n (5 in the beginning) is greater than 1, so go to the else statement.
in the else statement I'll have a constant (countArray) assigned to do countup(n - 1), which
is 4.
the next operation js encounters is countArray.push(n), so it will push 4 into an array.
then it encounters return countArray, which is 4.
the function continues with 3,2,1 till the base condition reaches n==0, and in that case the
function ends returning me an empty array.
I know this is not how it works, I'm simply "describing" how my noob mind works looking at this recursive function.
I looked at the solution of the exercise and other people's explanations, but I cannot understand why the function does not work like I described, and also why after reaching n==0, it starts to fill up an array counting up from 1 to 5.
I would like to understand this.
Realise that every execution of countup will have its own n and countArray variables.
It may help to visualise it. Each execution context is visualised as a "box". The variables in the outer boxes will still be there, when a function call returns.
The outermost box is the execution context that is created by the initial call: countup(5):
// n is 5 and does not change
const countArray = countup(n - 1);
+-------------------------------------------------------------------------------+
| // n is 4 and does not change |
| const countArray = countup(n - 1); |
| +-------------------------------------------------------------------------+ |
| | // n is 3 and does not change | |
| | const countArray = countup(n - 1); | |
| | +-------------------------------------------------------------------+ | |
| | | // n is 2 and does not change | | |
| | | const countArray = countup(n - 1); | | |
| | | +-------------------------------------------------------------+ | | |
| | | | // n is 1 and does not change | | | |
| | | | const countArray = countup(n - 1); | | | |
| | | | +-------------------------------------------------------+ | | | |
| | | | | // n is 0 and does not change | | | | |
| | | | | return []; // the if-block is executed because n < 1 | | | | |
| | | | +-------------------------------------------------------+ | | | |
| | | | // countArray is [] | | | |
| | | | countArray.push(n); // n is still 1 | | | |
| | | | return countArray; // returns [1] | | | |
| | | +-------------------------------------------------------------+ | | |
| | | // countArray is [1] | | |
| | | countArray.push(n); // n is still 2 | | |
| | | return countArray; // returns [1, 2] | | |
| | +-------------------------------------------------------------------+ | |
| | // countArray is [1, 2] | |
| | countArray.push(n); // n is still 3 | |
| | return countArray; // returns [1, 2, 3] | |
| +-------------------------------------------------------------------------+ |
| // countArray is [1, 2, 3] |
| countArray.push(n); // n is still 4 |
| return countArray; // returns [1, 2, 3, 4] |
+-------------------------------------------------------------------------------+
// countArray is [1, 2, 3, 4]
countArray.push(n); // n is still 5
return countArray; // returns [1, 2, 3, 4, 5]
You can just add some logging to visualize what is happening:
function countup(n) {
if (n < 1) {
console.log('n = %d, returning empty array', n);
return [];
} else {
console.log('n = %d, calling countup(%d - 1)', n, n);
const countArray = countup(n - 1);
console.log('n = %d, countArray is %s', n, JSON.stringify(countArray))
console.log('n = %d, pushing n onto array', n);
countArray.push(n);
console.log('n = %d, returning %s', n, JSON.stringify(countArray));
return countArray;
}
}
console.log(countup(5));
Here what the array looks like inside of each function call if this helps:
Array: 1 N: 1
Array: 1,2 N: 2
Array: 1,2,3 N: 3
Array: 1,2,3,4 N: 4
Array: 1,2,3,4,5 N: 5
If you put a console log in here like this, you can visualize it better.
function countup(n) {
if (n < 1) {
return [];
} else {
const countArray = countup(n - 1);
countArray.push(n);
console.log(`countup(${n}) returns ${countArray}`);
return countArray;
}
}
console.log(countup(5));
What happens is that as soon as you hit that first invocation of countup, execution moves there. So, invoke it with 5 and you see the first output statement is run with a 1, because several more times it basically called itself.
countup (5) calls countup (4) calls countup(3) calls countup (2) calls countup (1) --
countup (1) returns 1, back to the caller of 2 which returns 2, 1, back to the caller of 3 returns 3, 2, 1...and so on back up to 5.
I want to concatenate the bits of two number values like this:
+---------+---------+---------+-----------------+
| | val1 | val2 | concatenate val |
+---------+---------+---------+-----------------+
| decimal | 18 | 16 | 592 |
| hexa | 0x12 | 0x10 | 0x250 |
| binary | 0b10010 | 0b10000 | 0b1001010000 |
+---------+---------+---------+-----------------+
I have try to just concatenate with + like that :
const test = 0b10010, test1 = 0b10000
console.log(test+test1)//return 34
It does not concatenate the values but adds them together.
You could shift the first value by the bitwise length of the second value before adding.
const
add = (a, b) => (a << Math.ceil(Math.log2(b)) + 1) + b;
test = 0b10010,
test1 = 0b10000,
console.log(add(test, test1)); //
How many ways can you make the sum of a number?
From wikipedia: https://en.wikipedia.org/wiki/Partition_(number_theory)#
In number theory and combinatorics, a partition of a positive integer n, also called an integer partition, is a way of writing n as a sum of positive integers. Two sums that differ only in the order of their summands are considered the same partition. If order matters, the sum becomes a composition. For example, 4 can be partitioned in five distinct ways:
4
3 + 1
2 + 2
2 + 1 + 1
1 + 1 + 1 + 1
Examples
Basic
sum(1) // 1
sum(2) // 2 -> 1+1 , 2
sum(3) // 3 -> 1+1+1, 1+2, 3
sum(4) // 5 -> 1+1+1+1, 1+1+2, 1+3, 2+2, 4
sum(5) // 7 -> 1+1+1+1+1, 1+1+1+2, 1+1+3, 1+2+2, 1+4, 5, 2+3
sum(10) // 42
Explosive
sum(50) // 204226
sum(80) // 15796476
sum(100) // 190569292
My Attempt
I tried to loop through two arrays simultaneously and test them against eachother. This doesn't work (at least in the way I did it) for a few reasons.
My Code:
function sum(num, arr = []) {
if(num == 0){
return testNumbers(arr, num);
}
arr.push(num);
return sum(num - 1, arr);
function testNumbers(arrr, n){
let arr2 = [...arrr];
let count = 0;
let calculations = arrr.filter((item)=>{
return item + arr2.map((a)=>{
return a;
}) == n;
})
console.log(calculations);
}
}
console.log(sum(10));
You don't need to fix my code, as I don't think its salvageable, but how do you solve the problem?
This is in fact a fairly simple recursion, if we think of the problem as counting the partitions with a given lower bound. We have two simple bases cases of 0 and 1 if the lower bound is greater than our target number or equal to it. Otherwise we recur in two ways, one for when we use that lower bound, and one for when we don't. Here's one version, where lb is the lower bound of the numbers to use:
const count = (n, lb = 1) =>
lb > n
? 0
: lb == n
? 1
: count (n - lb, lb) + count (n, lb + 1)
This counts the number of partitions with the given lower bound. So count(10, 3) would yield 5, one for each array in [[3, 3, 4], [3, 7], [4, 6], [5, 5], [10]]. Although the default value for the lower bound means that we can call it with just our target number, there are potential issues with this if we tried, say, to map over it. So it might be best to wrap this in a separate function, const countPartitions = (n) => count (n, 1)
const count = (n, lb) =>
lb > n
? 0
: lb == n
? 1
: count (n - lb, lb) + count (n, lb + 1)
const countPartitions = (n) => count (n, 1)
console .log ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .map (countPartitions))
But this will be quite slow for larger input. My test for 100 took 56.3 seconds.) If we memoize the intermediate results, we should speed things up a great deal. We can do this manually or, as I'd prefer, with a memoization helper:
const memoize = (makeKey, fn) => {
const memo = {}
return (...args) => {
const key = makeKey (...args)
return memo [key] || (memo [key] = fn (...args))
}
}
const count = memoize (
(n, lb) => `${n}~${lb}`,
(n, lb) =>
lb > n
? 0
: lb == n
? 1
: count (n - lb, lb) + count (n, lb + 1)
)
const countPartitions = (n) => count (n, 1)
console .log (countPartitions (100))
And this now takes 20 milliseconds in my test.
Update: answering comment
A comment asked
Hey Scott, sorry to bring up an old post, but I've been going over your solution here and I'm afraid I'm having trouble understanding exactly how it works. If you don't mind, could you go a little more in-depth on why counting instances of n===lb leads to the answer? Maybe my math is just weak, but I'm not following the partitions logic.
Let's imagine we're trying to partition 10, and we've already counted those whose lowest value is 1 and those whose lowest value is 2, and now we're trying to count the partitions where the lowest value is at least 3.
We call count (10, 3). Since 3 > 10 is false, we don't return 0. And since 3 == 10 is false, we don't return 1. Instead we make two recursive calls and add their results together. The first one is where we choose to use 3 in our output, choose (7, 3), since we will have seven remaining when we've selected the first 3. The second one is where we choose not to use 3 in our output, choose (10, 4), since the lowest bound will be 4 if we are to skip 3, but we still have ten to partition.
The call structure would look like this:
(10, 3)
___________________________________/\____________________
/ \
(7, 3) + (10, 4)
___________/\___________ __________/\_________
/ \ / \
(4, 3) + (7, 4) (6, 4) + (10, 5)
____/\___ ________/\_______ ____/\____ _____/\_______
/ \ / \ / \ / \
(1, 3) + (4, 4) (3, 4) + (7, 5) (2, 4) + (6, 5) (5, 5) + (10, 6)
| | | ___/\___ | ___/\___ | _____/\_____
| | | / \ | / \ | / \
| | | (2, 5) + (7, 6) | (1, 5) + (6, 6) | (4, 6) + (10, 7)
| | | | __/\__ | | | | | ____/\____
| | | | / \ | | | | | / \
| | | | (1, 6) + (7, 7) | | | | | (3, 7) + (10, 8)
| | | | | | | | | | | | ___/\____
| | | | | | | | | | | | / \
| | | | | | | | | | | | (2, 8) + (10, 9)
| | | | | | | | | | | | | ___/\___
| | | | | | | | | | | | | / \
| | | | | | | | | | | | | (1, 9) + (10, 10)
| | | | | | | | | | | | | | |
| | | | | | | | | | | | | | |
(3>1) (4=4) (4>3) (5>2) (6>1) (7=7) (4>2) (5>1) (6=6)(5=5) (6>4) (7>3) (8>2) (9>1) (10=10)
| | | | | | | | | | | | | | |
| | | | | | | | | | | | | | |
0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + 0 + 1
| | | | |
| | | | |
[[3, 3, 4], [3, 7], [4, 6],[5, 5], [10]]
We can get a speed up over Scott's solution by using the recurrence relation for the partition function that uses pentagonal numbers:
function _p(k, memo){
if (k == 0)
return 1;
if (memo[k])
return memo[k];
let result = 0;
let i = 1;
const ms = [1, 1, -1, -1];
while (true){
const n = i & 1 ? (i + 1) / 2 : -i / 2;
const pentagonal = (3*n*n - n) / 2;
if (pentagonal > k)
break;
result = result + ms[(i-1) % 4] * _p(k - pentagonal, memo);
i = i + 1;
}
return memo[k] = result;
}
function p(k){
return _p(k, {});
}
var ks = [1, 2, 3, 4, 5, 6, 10, 50, 80, 100];
for (let k of ks){
const start = new Date;
console.log(`${ k }: ${ p(k) }`);
console.log(`${ new Date - start } ms`);
console.log('');
}
I watched a youtube video where this basic permutation tree was shown. If you look at this bit of code:
function recursion(input, set = [], result = []) {
if (!input.length) {
result.push([...set].join(''));
}
for (let i = 0; i < input.length; i++) {
const newArr = input.filter((n, index) => index !== i);
set.push(input[i]);
recursion(newArr, set, result);
set.pop();
}
return result.join(', ');
}
you can see that the base case (if statement) is at the top before the parameter nums is filtered. So, my whole question is how the tree and the code makes sense because for me the code would remove one too many digits from the set array. Becuase it pops an item of when returning and doesn't it return more than two times?
Does this log add clarity?
/ entering recursion with input = [1,2,3], set = [], result = []
| looping, i = 0
| adding 1 to set
| / entering recursion with input = [2,3], set = [1], result = []
| | looping, i = 0
| | adding 2 to set
| | / entering recursion with input = [3], set = [1,2], result = []
| | | looping, i = 0
| | | adding 3 to set
| | | / entering recursion with input = [], set = [1,2,3], result = []
| | | | adding 123 to result
| | | \ returning [123]
| | | removing 3 from set
| | \ returning [123]
| | removing 2 from set
| | looping, i = 1
| | adding 3 to set
| | / entering recursion with input = [2], set = [1,3], result = [123]
| | | looping, i = 0
| | | adding 2 to set
| | | / entering recursion with input = [], set = [1,3,2], result = [123]
| | | | adding 132 to result
| | | \ returning [123,132]
| | | removing 2 from set
| | \ returning [123,132]
| | removing 3 from set
| \ returning [123,132]
| removing 1 from set
| looping, i = 1
| adding 2 to set
| / entering recursion with input = [1,3], set = [2], result = [123,132]
| | looping, i = 0
| | adding 1 to set
| | / entering recursion with input = [3], set = [2,1], result = [123,132]
| | | looping, i = 0
| | | adding 3 to set
| | | / entering recursion with input = [], set = [2,1,3], result = [123,132]
| | | | adding 213 to result
| | | \ returning [123,132,213]
| | | removing 3 from set
| | \ returning [123,132,213]
| | removing 1 from set
| | looping, i = 1
| | adding 3 to set
| | / entering recursion with input = [1], set = [2,3], result = [123,132,213]
| | | looping, i = 0
| | | adding 1 to set
| | | / entering recursion with input = [], set = [2,3,1], result = [123,132,213]
| | | | adding 231 to result
| | | \ returning [123,132,213,231]
| | | removing 1 from set
| | \ returning [123,132,213,231]
| | removing 3 from set
| \ returning [123,132,213,231]
| removing 2 from set
| looping, i = 2
| adding 3 to set
| / entering recursion with input = [1,2], set = [3], result = [123,132,213,231]
| | looping, i = 0
| | adding 1 to set
| | / entering recursion with input = [2], set = [3,1], result = [123,132,213,231]
| | | looping, i = 0
| | | adding 2 to set
| | | / entering recursion with input = [], set = [3,1,2], result = [123,132,213,231]
| | | | adding 312 to result
| | | \ returning [123,132,213,231,312]
| | | removing 2 from set
| | \ returning [123,132,213,231,312]
| | removing 1 from set
| | looping, i = 1
| | adding 2 to set
| | / entering recursion with input = [1], set = [3,2], result = [123,132,213,231,312]
| | | looping, i = 0
| | | adding 1 to set
| | | / entering recursion with input = [], set = [3,2,1], result = [123,132,213,231,312]
| | | | adding 321 to result
| | | \ returning [123,132,213,231,312,321]
| | | removing 1 from set
| | \ returning [123,132,213,231,312,321]
| | removing 2 from set
| \ returning [123,132,213,231,312,321]
| removing 3 from set
\ returning [123,132,213,231,312,321]
You can see how I added the logging to your code in this snippet:
const log = (depth, message) =>
console .log ('| '.repeat (depth) + message)
function recursion(input, set = [], result = [], depth = 0) {
log (depth, `/ entering recursion with input = [${input}], set = [${set}], result = [${result}]`)
if (!input.length) {
log (depth, `| adding ${[...set].join('')} to result`)
result.push([...set].join(''));
}
for (let i = 0; i < input.length; i++) {
log (depth, `| looping, i = ${i}`)
const newArr = input.filter((n, index) => index !== i);
log (depth, `| adding ${input[i]} to set` )
set.push(input[i]);
recursion(newArr, set, result, depth + 1);
log (depth, `| removing ${input[i]} from set` )
set.pop();
}
log (depth, `\\ returning [${result}]`)
return result.join(', ');
}
console .log (recursion([1, 2, 3]))
.as-console-wrapper {min-height: 100% !important; top: 0}
(but the console output there is limited to the last 50 lines.)
I need to get the "payment type and the customer type from this kind of string
Examples:
| D | A | B | C |
| NZ | AAA | BBB | NZ |
| AZ | CCC | DDD | AZ |
| CA | EEE | FFF | CA |
should I try the get the pattern and write a function for this? or I can find some library to detect it
so the output should be
{payment:["AAA","CCC",'EEE'],
customer:["BBB",'DDD","FFF"]}
function detect(str){
let countBar=1
let countBar2=0
let paymentLoc=NaN
let customerLoc=NaN
let after =0
let arr1=str.split(" ")
arr1=arr1.filter(item=>{return item!==""})
let newStr=''
for(let i=0;i<arr1.length;i++){
arr1[i].trim()
if(arr1[i]==='|'){
countBar++
}
if(arr1[i]==="||"){
countBar2++
}
if(arr1[i].includes("payment")){
paymentLoc=i
}
after=((countBar/(countBar2))-1)*2
let sol=[]
for(let i=0;i<arr1.length;i++){
if(arr1[i].includes("payment")){
console.log('payment index',i)
sol.push(arr1[i+after+1])
}
if(arr1[i].includes("customer")){
console.log('customer index',i)
sol.push(arr1[i+after+1])
}
}
newStr=arr1.join('')
console.log(newStr)
}
I had some fun with this one. My first thought was to use an npm package, since the string looks a lot like CSV with | as the delimiter. The package csvtojson is a good one, but why use a well-regarded library when you can hack something together?
Here's my first attempt (in Typescript):
const exampleString = ` | market | payment type | customer type | translation |
| NZ | AAA | BBB | NZ |
| AZ | CCC | DDD | AZ |
| CA | EEE | FFF | CA |`;
const cleanColumn = (col: string) =>
col
.replace("|", "")
.trim()
.replace(/\s/, "_");
const cleanRow = (row: string) =>
row
.split(/\s\|/)
.map(cleanColumn)
.filter(Boolean);
const pivotRows = (
pivoted: string[][],
currentRow: string[],
rowIndex: number
) => {
if (rowIndex === 0) {
currentRow.forEach((col, colIndex) => {
pivoted[colIndex] = [col];
});
} else {
currentRow.forEach((col, colIndex) => {
pivoted[colIndex].push(col);
});
}
return pivoted;
};
const buildObject = (
obj: { [key: string]: string[] },
currentRow: string[]
) => {
let currentCol: string;
currentRow.forEach((col, index) => {
if (index === 0) {
currentCol = col;
obj[currentCol] = [];
} else {
obj[currentCol].push(col);
}
});
return obj;
};
const detect = (str: string) =>
str
.split("\n")
.map(cleanRow)
.reduce(pivotRows, [])
.reduce(buildObject, {});
console.log(detect(exampleString));
If you look at detect, it's just executing a series of functions on the string. The first one just splits it by line-breaks. I liked what you were going for with the countBar variables, but this seemed easier.
That gives us a bunch of string arrays, which need to be broken down into columns. Here, I used some RegEx to separate everything between a combination of space and |. In cleanColumn(), I remove the remaining |s in case there are any stragglers, then replace the spaces with underscores so they can be used as object keys.
Then, remove the empty strings with the .filter(Boolean) trick (link). The last two functions are probably more verbose than necessary, but they do the job. pivotRows() uses the row and column indexes to pivot columns into rows. Last, in buildObject() the first element of each of the rows is added as the key of an object, with the rest of the values being pushed into string arrays.
Really, you should probably just use csvtojson.
Here's an attempt at just splitting by |.
var text = `| market | payment type | customer type | translation |
| NZ | AAA | BBB | NZ |
| AZ | CCC | DDD | AZ |
| CA | EEE | FFF | CA |`;
var result = {
payment: text.split('|').filter((f, i) => i-7 >= 0 && (i - 7) % 5 == 0)
.map(p => p.trim()),
customer: text.split('|').filter((f, i) => i-8 >= 0 && (i - 8) % 5 == 0)
.map(p => p.trim())
}
console.log(result)