Conditional functions in Redux state - javascript

I have built an incrementing button to increase the string ( summing 20 to each number starting from n 3 ) in the path d for SVG elements.
The increment button does the following in my reduce function and it works fine
export default function svgs(state = [], action) {
switch(action.type) {
case 'INCREMENT_COORDINATES' :
console.log("incrementing coordinaates");
const a = action.index;
let string = state[a].d;
let array = string.split(" ");
let max = array.length;
let last = max - 2;
let i = (state[a].index || 3) + 1;
if ( i === last ) i = 3;
if (array[i] !== 'M' && array[i] !== 'L') array[i] = parseInt(array[i]) + 20;
return [
...state.slice(0,a), // before the one we are updating
{...state[a], d: array.join(' '), index: i}, // updating
...state.slice(a + 1), // after the one we are updating
]
default:
return state;
}
}
My struggle is that in the SVG component the path d can consume also different data objects
For example
apart from
path d={svg.d}
it can also have
path d={svg.d2}
path d={svg.d3}
See the snippet example
<svg>
<svg>
<g>
<path d={svg.d}></path>
</g>
</svg>
<g>
<path d={svg.d2}></path>
<path d={svg.d3}></path>
</g>
</svg>
How can I modify my redux function to increment only the string or strings of that particular SVG element
I have some svg elements which can have all svg objects ( svg.d, svg.d2, svg.d3), or only two ( svg.d, svg.d2 ) or one ( svg.d ).
For example, if I modify that redux function above, it can be something like
let string = state[a].d || state[a].d2 || state[a].d3;
and then
return [
...state.slice(0,a), // before the one we are updating
{...state[a], d: array.join(' '), index: i, d2: array.join(' '), index: i, d3: array.join(' '), index: i }, // updating
...state.slice(a + 1), // after the one we are updating
]
The only problem here is that if one of the SVG elements has one or two empty objects between svg.d, svg.d2, svg.d3, after the click action it gets the same values of the string modified, and I do not want this.
Hope explanation is clear, I will set up a jsBin if it is necessary.

You could map over the svg objects and transform them individually (if they exist):
const aObj = state[a];
const [d, d2, d3] = [aObj.d, aObj.d2, aObj.d3].map((string) => {
if(!string) return null;
let array = string.split(" ");
let max = array.length;
let last = max - 2;
let i = (state[a].index || 3) + 1;
if ( i === last ) i = 3;
if (array[i] !== 'M' && array[i] !== 'L') array[i] = parseInt(array[i]) + 20;
return array.join(" ");
});
return [
...state.slice(0,a), // before the one we are updating
{...aObj, d, d2, d3}, // updating
...state.slice(a + 1), // after the one we are updating
]
I'm not sure I completely understand the question though.

Related

Sum of all nodes in a binary tree giving incorrect output in JavaScript

I am trying to solve the following problem.
Problem statement: Given a binary tree, return the sum of the depth of all the nodes. For example, this is a binary tree.
1
/ \
2 3
/ \ / \
4 5 6 7
/ \
8 9
The depth of the node with value 2 is 1.
Similarly, the Depth of node with value 4 is 2 etc...
The total depth of all nodes is 16.
I have written the following recursive solution which is giving me the incorrect output.
My pseudocode is as follows :
If the current node has no left and right child, then return;
Check if the current node has any left child. If it has, then:
Add 1 to depthSoFar variable
Add the depthSoFar value to totalDepth variable
Then, recursively call the function with the left child as the current node
If the current node has the right child, then:
Add 1 to depthSoFar variable
Add the depthSoFar value to totalDepth variable
Then, recursively call the function with the right child as the current node
The code is as follows;
var nodeDepths = function(root) {
var totalDepth = 0;
function depth(root, depthSoFar) {
if (root.left === null && root.right === null) {
return;
}
// checking if it has left child
if(root.left) {
depthSoFar = depthSoFar + 1;
// Add it to total
totalDepth = totalDepth + depthSoFar;
depth(root.left, depthSoFar);
}
if(root.right) {
depthSoFar = depthSoFar + 1;
totalDepth = totalDepth + depthSoFar;
depth(root.right, depthSoFar);
}
}
depth(root, 0);
return totalDepth;
};
My output is 22 but the correct output is 16.
var nodeDepths = function(root) {
var totalDepth = 0;
function depth(root, depthSoFar) {
if (root.left === null && root.right === null) {
return;
}
// checking if it has left child
if(root.left) {
depthSoFar = depthSoFar + 1;
// Add it to total
totalDepth = totalDepth + depthSoFar;
depth(root.left, depthSoFar);
}
if(root.right) {
depthSoFar = depthSoFar + 1;
totalDepth = totalDepth + depthSoFar;
depth(root.right, depthSoFar);
}
}
depth(root, 0);
return totalDepth;
};
class BinaryTree {
constructor(value) {
this.value = value;
this.left = null;
this.right = null;
}
}
var root = new BinaryTree(1);
root.left = new BinaryTree(2);
root.right = new BinaryTree(3);
root.left.left = new BinaryTree(4);
root.left.right = new BinaryTree(5);
root.right.left = new BinaryTree(6);
root.right.right = new BinaryTree(7);
root.left.left.left = new BinaryTree(8);
root.left.left.right = new BinaryTree(9);
console.log(nodeDepths(root))
I am not understanding where I am getting wrong. Any help in understanding the problem, would be appreciated. Thanks in advance.
sumDepths has a simple inductive definition
if the input tree, t, is empty, return the empty sum, zero.
(inductive) the tree has at least one node. return the current depth, d, plus the sum of the recursive sub-problems, sumDepths(t.left, d + 1) plus sumDepths(t.right, d + 1)
function sumDepths (t, d = 0) {
if (t == null)
return 0 // #1
else
return d + sumDepths(t.left, d + 1) + sumDepths(t.right, d + 1) // #2
}
function node (value, left, right) {
return { value, left, right }
}
const tree =
node
( 1
, node
( 2
, node(4, node(8), node(9))
, node(5)
)
, node(3, node(6), node(7))
)
console.log(sumDepths(tree)) // 16
Another way to think about the problem is write a depth traverse generator and sum each depth -
function* traverse (t, d = 0) {
if (t == null) return
yield [d, t.value]
yield *traverse(t.left, d + 1)
yield *traverse(t.right, d + 1)
}
function node (value, left, right) {
return { value, left, right }
}
const tree =
node
( 1
, node
( 2
, node(4, node(8), node(9))
, node(5)
)
, node(3, node(6), node(7))
)
const sum =
Array
.from(traverse(tree), ([ depth, value ]) => depth)
.reduce((r, d) => r + d, 0)
console.log(sum) // 16

Design a layout sorting algorithm

I'm trying to come up with an algorithm to make sure my items fit into the screen. These items are randomly generated and have a score of either 1, 2, or 4. This score lets met know how much space they take up on the screen. Score 4 means it takes up 4x the size of a score 1 item. It's important to note that an item with a score of 1 always needs a 'buddy', so they take up the width of the page.
I want to fit up to a score of 6 on my page, to an infinite amount of pages.
A valid result could be [ [1, 1, 2, 2], [4, 2] ]. Each array in the root array is its own page, so this would mean I have 2 pages that are properly filled since their child scores count up to 6.
Result:
This is what I came up with, but it's not so bulletproof. I'm not so good at writing these kinds of algorithms. Perhaps someone could help me in the right direction?
It's okay to skip items like I'm doing, but I'm also skipping items that might find a match later. Currently, all skipped items will be added to the front of items before sortItems is called again. So they will just be sorted with the next batch of items to find a new match.
const getItemScore = (size) => {
switch (size) {
case 'small':
return 1;
case 'medium':
return 2;
case 'large':
return 4;
default:
console.error('Unknown size:', size);
}
}
const sortItems = (items) => {
const maxPageScore = 6;
let sorted = [];
let perPage = [];
let smallSort = [];
let skippedItems = [];
let currentPageScore = 0;
items.forEach((item) => {
const itemScore = getItemScore(item.size);
if (currentPageScore + itemScore < maxPageScore) {
// Item fits in page
if (item.size === 'small') {
// A small item needs to have a 'buddy'
if (smallSort.length < 2) {
// the small item can be added
smallSort.push(item);
}
if (smallSort.length === 2) {
// we have found a buddy for the item
smallSort.forEach((smallItem) => {
perPage.push(smallItem);
});
// reset smallSort
smallSort = [];
// increment pageScore
currentPageScore += 2;
}
}
else if (item.size === 'medium' || item.size === 'large') {
// medium and large can always be added if their score isnt more than maxPageScore
perPage.push(item);
currentPageScore += itemScore;
}
}
else if (currentPageScore + itemScore === maxPageScore) {
// we have a full page
perPage.push(item);
sorted.push(perPage);
// reset values
perPage = [];
smallSort = [];
currentPageScore = 0;
}
else {
// item doesnt fit in page
skippedItems.push(item);
}
});
// done looping over items
const result = {
sorted: sorted,
skipped: skippedItems,
};
return result;
}
The intuition would tell us to come up with a recurrence-relation so we can reduce the problems into smaller-subproblems.
Lets say we are calculating for size = N
The top-section could either be:
2 blocks of 1: [1,1] ; which leaves us with size = N - 1
1 block of 2: 2 ; which leaves us with size = N - 1
1 block of 4: 4 ; which leaves us with size = N - 2
Say, S(n) = #ways to arrange these blocks, then:
S(n) = S(n-1) + S(n-1) + S(n-2)
^from-(1) ^from-(2) ^from-(3)
which becomes:
S(n) = 2S(n-1) + S(n-2)
with boundary conditions:
S(0) = 0
S(1) = 2
This makes it quite easy to solve:
def get_pattern_combs(N):
if N < 1:
return 0
if N == 1:
return 2
lval = 0
val = 2
for i in range(2, N+1):
new_val = 2*val + lval
lval = val
val = new_val
return val

Saving count variable through recursion with only 1 argument

What's the best way to "save" the returned variable from the previous stack all the way to the first call using only one argument?
I know of 2 techniques to 'save' variables in recursion, but the test cases don't let me implement them that way.
Prompt: reverse a string using recursion.
Test cases:
should be invoked with one argument
should use recursion by calling itself
Attempt 1 (using helper function):
var reverse = function(string) {
var str = string.split('');
var reversed = [];
var helper = function(i) {
reversed.unshift(str[i]);
if (i < str.length) {
i++;
helper(i);
}
};
helper(0);
return reversed.join('');
}
Attempt 2 (without helper + using extra arguments)
var reverse = function(string, index, prev) {
var prev = prev || [];
index = index || 0;
if (index < string.length) {
prev.unshift(string[index]);
index++;
reverse(string, index, prev);
}
return prev.join('');
};
What would be the 3rd way of doing this?
Thanks!
Source: #9 from https://github.com/JS-Challenges/recursion-prompts/blob/master/src/recursion.js
You don't need to save anything. If you order the return correctly the call stack will unwind and create the reversed string for you:
var reverse = function(string) {
if (string.length == 0) return string // base case
return reverse(string.slice(1)) + string[0] // recur
};
console.log(reverse("hello"))
By returning the result of the recursion before the first character you wind and unwind the stack before the first call returns. You can then get result without maintaining any state other than the call stack.
I'd store the information to be used later in the function body only, without passing it down the call chain:
var reverse = function(string) {
const { length } = string;
return string[length - 1] + (
string.length === 1
? ''
: reverse(string.slice(0, length - 1))
);
};
var reverse = function(string) {
const { length } = string;
return string[length - 1] + (
string.length === 1
? ''
: reverse(string.slice(0, length - 1))
);
};
console.log(reverse('foo bar'));
Others have shown better ways to write your reverse recursively.
But as to the actual question you asked, modern JS allows for default arguments. I tend not to use them very much, but they are very useful in JS to allow you to write this sort of recursion without helper functions. Thus,
const reverse = (string, index = 0, prev = []) => {
if (index < string.length) {
prev .unshift (string [index])
reverse (string, index + 1, prev)
}
return prev .join ('')
}
console .log (
reverse ('abcde')
)
Again, other answers have better versions of reverse. But this should show you how you can have a function that takes only one public variable and yet still uses its extra arguments.
Here's another way you can do it using destructuing assignment and a technique called continuation-passing style -
const cont = x =>
k => k (x)
const Empty =
Symbol ()
const reverse = ([ s = Empty, ...more ]) =>
s === Empty
? cont ("")
: reverse
(more)
(rev => cont (rev + s))
reverse ("hello world") (console.log)
// dlrow olleh
But watch out for really big strings -
const bigString =
"abcdefghij" .repeat (1000)
reverse (bigString) (console.log)
// RangeError: Maximum call stack size exceeded
Here's another technique called a trampoline which allows to think about the problem recursively but have a program that is both fast and stack-safe. Have your cake and eat it, too -
const recur = (...values) =>
({ recur, values })
const loop = f =>
{ let r = f ()
while (r && r.recur === recur)
r = f (...r.values)
return r
}
const reverse = (s = "") =>
loop // begin loop ...
( ( r = "" // state variable, result
, i = 0 // state variable, index
) =>
i >= s.length // terminating condition
? r // return result
: recur // otherwise recur with ...
( s[i] + r // next result
, i + 1 // next index
)
)
const bigString =
"abcdefghij" .repeat (1000)
console .log (reverse (bigString))
// jihgfedcba...jihgfedcba

I need to extract every nth char of a string in Javascript

Ive been reading everything online but its not exactly what I need
var x = 'a1b2c3d4e5'
I need something to get me to
using 1 the answer should be abcde
using 2 the answer should be 12345
using 3 the answer should be b3e
the idea behind it if using 1 it grabs 1 skips 1
the idea behind it if using 2 it grabs 2 skips 2
the idea behind it if using 3 it grabs 3 skips 3
I dont want to use a for loop as it is way to long especially when your x is longer than 300000 chars.
is there a regex I can use or a function that Im not aware of?
update
I'm trying to some how implement your answers but when I use 1 that's when I face the problem. I did mention trying to stay away from for-loops the reason is resources on the server. The more clients connect the slower everything becomes. So far array.filter seem a lot quicker.
As soon as I've found it I'll accept the answer.
As others point out, it's not like regular expressions are magic; there would still be an underlying looping mechanism. Don't worry though, when it comes to loops, 300,000 is nothing -
console.time('while')
let x = 0
while (x++ < 300000)
x += 1
console.timeEnd('while')
// while: 5.135 ms
console.log(x)
// 300000
Make a big string, who cares? 300,000 is nothing -
// 10 chars repeated 30,000 times
const s =
'abcdefghij'.repeat(30000)
console.time('while string')
let x = 0
let interval = 2
let values = []
while (x < s.length)
{ values.push(s[x])
x += interval
}
let result = values.join('')
console.timeEnd('while string')
// while string: 31.990ms
console.log(result)
console.log(result.length)
// acegiacegiacegiacegiacegiacegiacegiacegia...
// 150000
Or use an interval of 3 -
const s =
'abcdefghij'.repeat(30000)
console.time('while string')
let x = 0
let interval = 3
let values = []
while (x < s.length)
{ values.push(s[x])
x += interval
}
let result = values.join('')
console.timeEnd('while string')
// while string: 25.055ms
console.log(result)
console.log(result.length)
// adgjcfibehadgjcfibehadgjcfibehadgjcfibe...
// 100000
Using a larger interval obviously results in fewer loops, so the total execution time is lower. The resulting string is shorter too.
const s =
'abcdefghij'.repeat(30000)
console.time('while string')
let x = 0
let interval = 25 // big interval
let values = []
while (x < s.length)
{ values.push(s[x])
x += interval
}
let result = values.join('')
console.timeEnd('while string')
// while string: 6.130
console.log(result)
console.log(result.length)
// afafafafafafafafafafafafafafafafafafafafafafa...
// 12000
You can achieve functional style and stack-safe speed simultaneously -
const { loop, recur } = require('./lib')
const everyNth = (s, n) =>
loop
( (acc = '', x = 0) =>
x >= s.length
? acc
: recur(acc + s[x], x + n)
)
const s = 'abcdefghij'.repeat(30000)
console.time('loop/recur')
const result = everyNth(s, 2)
console.timeEnd('loop/recur')
// loop/recur: 31.615 ms
console.log(result)
console.log(result.length)
// acegiacegiacegiacegiacegiacegiacegia ...
// 150000
The two are easily implemented -
const recur = (...values) =>
({ recur, values })
const loop = f =>
{ let acc = f()
while (acc && acc.recur === recur)
acc = f(...acc.values)
return acc
}
// ...
module.exports =
{ loop, recur, ... }
And unlike the [...str].filter(...) solutions which will always iterate through every element, our custom loop is much more flexible and receives speed benefit when a higher interval n is used -
console.time('loop/recur')
const result = everyNth(s, 25)
console.timeEnd('loop/recur')
// loop/recur: 5.770ms
console.log(result)
console.log(result.length)
// afafafafafafafafafafafafafafa...
// 12000
const recur = (...values) =>
({ recur, values })
const loop = f =>
{ let acc = f()
while (acc && acc.recur === recur)
acc = f(...acc.values)
return acc
}
const everyNth = (s, n) =>
loop
( (acc = '', x = 0) =>
x >= s.length
? acc
: recur(acc + s[x], x + n)
)
const s = 'abcdefghij'.repeat(30000)
console.time('loop/recur')
const result = everyNth(s, 2)
console.timeEnd('loop/recur')
// loop/recur: 31.615 ms
console.log(result)
console.log(result.length)
// acegiacegiacegiacegiacegiacegiacegia ...
// 150000
Since I'm not an expert of regex, I'd use some fancy es6 functions to filter your chars.
var x = 'a1b2c3d4e5'
var n = 2;
var result = [...x].filter((char, index) => index % n == 0);
console.log(result);
Note that because 0 % 2 will also return 0, this will always return the first char. You can filter the first char by adding another simple check.
var result = [...x].filter((char, index) => index > 0 && index % n == 0);
As a variant:
function getNth(str, nth) {
return [...str].filter((_, i) => (i + 1) % nth === 0).join('');
}
console.log(getNth('a1b2c3d4e5', 2)); // 12345
console.log(getNth('a1b2c3d4e5', 3)); // b3e
What I'd suggest, to avoid having to iterate over the entire array, is to step straight into the known nth's.
Here's a couple of flavors:
function nthCharSubstr(str, nth) {
let res = "";
for (let i = nth - 1; i < str.length; i += nth) {
res += string[i];
}
return res;
}
More ES6-y:
const nthCharSubstr = (str, nth) =>
[...Array(parseInt(str.length / nth)).keys()] // find out the resulting number of characters and create and array with the exact length
.map(i => nth + i * nth - 1) // each item in the array now represents the resulting character's index
.reduce((res, i) => res + str[i], ""); // pull out each exact character and group them in a final string
This solution considers this comment as being valid.

Algorithm: Change all values of array depending on one and keeping same sum

Dear all,
I am stuck with a problem I have to solve. I'm doing it in JavaScript, but this applied generally to any language and is more an algorithm issue than anything else.
Lets say I have an array with 5 values and the TOTAL of these values should always be 500 in the end. The start values are 100, 100, ... , 100.
Ok and now I want that for the case I change ONE value, the other values "adjust" in the way so the "total value" of 500 is kept. And they don't arrange in some random order, but keep they original position and "move" towards the balance so their original value is kept (a bit).
Example:
100 100 100 100 100
I set the first one to 0
Result should be:
0 125 125 125 125
Now I set the second to 0
Result should be:
31.25 0 156.25 156.25 156.25
I have a working prototype - but I am very unsatisfied with the results. And I believe it can be done a LOT easier, but I cant think of any.
Im attaching my JS source and its fully commented.
Here is the general idea:
INPUT:
- array: of N INT elemnt values
- changed: the index of the element that has been adjusted, this one will be ignored for the array adjustments
- arrayMin / arrayMax: the values that are considered limits for the array elements
- arraySum: defines the sum of the array - this is important to see to what SUM the array has to adjust
PROCESS:
- the array elements minus 1 (the one that is ignored) are counted
- the difference made by the one change of the whole sum is computed
- the difference that has to be made to one and each (except the changed) is computed
- now there is a loop which adds (or subtracts) the difference to each object
- if the object reaches its limits (min or max) nothing can be added or subtracted more and this element will be ingored for the rest computation
- what could not be added to these elements hitting the limit is saved in REST
- at the end the loop checks if there is any REST and if there is, the loops repeats with REST computed among elements that can and may be adjusted further
- NOTE: If the rest is really small - treat it
Should anyone be interested why and what for I need it - I was thinking of using four sliders that share one "total" value and you set them up according to your preferences and the others take values depending on the change.
Source:
JS source file
**Im open to ideas :) **
Thank you
Oliver
Without the min/max constraints the function could look like this:
function reMapArray(array, changed, arraySum) {
const sum = array.reduce( (a, b) => a+b );
const adjust = (sum - arraySum) / (array.length - 1);
return array.map( (a, i) => i === changed ? a : a - adjust );
}
// Demo use
let array = [100, 100, 100, 100, 100];
array[0] = 0;
array = reMapArray(array, 0, 500);
console.log(array.toString());
array[1] = 0;
array = reMapArray(array, 1, 500);
console.log(array.toString());
With the min/max verification added, it could look like this:
function reMapArray(array, index, minValue, maxValue, arraySum) {
const sum = array.reduce( (a, b) => a+b );
if (sum === arraySum) return array; // end recursion: solution found
const adjust = (arraySum - sum) / array.reduce(
// count the values that can still be modified
(c, a, i) => c + (i === index ? 0
: arraySum > sum ? a < maxValue
: a > minValue),
0);
// apply adjustment, but without getting out of range, and then recurse
return reMapArray(array.map( (a, i) =>
i === index ? a : Math.max(minValue, Math.min(maxValue, a + adjust)) ),
index, minValue, maxValue, arraySum);
}
// Demo use:
let array = [100, 100, 100, 100, 100];
array[0] = 0;
array = reMapArray(array, 0, 0, 150, 500);
console.log(array.toString());
array[1] = 0;
array = reMapArray(array, 1, 0, 150, 500);
console.log(array.toString());
Here the second output is different than with the first solution, because the maximum value has been set to 150, so an output with 156.25 is not allowed.
Java solution for those interested:
public class ArrayAutoAdjuster {
private double[] values = new double[5];
public ArrayAutoAdjuster(){
for(int i = 0; i < values.length; i++){
values[i] = 100;
}
}
public static void main(String args[]){
ArrayAutoAdjuster aaa = new ArrayAutoAdjuster();
aaa.setNewValue(0,0);
System.out.println(aaa.toString());
aaa.setNewValue(1, 0);
System.out.println(aaa.toString());
}
public void setNewValue(int position, double value){
if(values[position] == value){
return;
}
double diff = (values[position] - value)/(values.length-1);
for(int i = 0; i < values.length; i++){
values[i] = i == position ? value : values[i] + diff;
}
}
public String toString(){
String s = "";
for(int i = 0; i < values.length; i++){
s += values[i];
if(i < values.length-1){
s+=",";
}
}
return s;
}
}
Here's a solution with some logging and error checking.
var sum = function(acc, itemValue) { return acc + itemValue; };
var setInitialArray = function(numItems, total) {
console.log("Create array of " + numItems + " items that sum to " + total);
var itemValue = Math.floor(total / numItems);
var extra = total - (numItems * itemValue);
var initArray = Array.apply(null, Array(5)).map(Number.prototype.valueOf, itemValue);
initArray[0] += extra;
return initArray;
};
var adjustArray = function(itemIdx, newValue, items) {
if (!Number.isInteger(itemIdx) || itemIdx < 0 || itemIdx >= items.length) return items;
console.log("Set item " + (itemIdx + 1) + " to " + newValue);
var total = items.reduce(sum, 0),
origItemValue = items[itemIdx],
diffValue = origItemValue - newValue,
totalForRemainItems = total + diffValue,
numItems = items.length - 1;
if (diffValue === 0 || totalForRemainItems < 0) return items;
// make copy of items without the changing item
var newItems = [].concat(items);
newItems.splice(itemIdx, 1);
var itemValue = Math.floor(totalForRemainItems / numItems);
var extra = totalForRemainItems - (numItems * itemValue);
newItems.forEach(function(item, idx) { newItems[idx] = (idx === 0) ? itemValue + extra : itemValue; });
newItems.splice(itemIdx, 0, newValue);
return newItems;
};
var myArray = setInitialArray(5, 502);
console.log(myArray);
var myNewArray = adjustArray(2, 50, myArray);
console.log(myNewArray);

Categories

Resources