Canvas not rendering In reactjs - javascript

I want to add canvas on the website that i am developing but i can seem to understand why the canvas in not showing up. what could be the issue?
Below is what i have tried. When i hover the mouse on the header it shows that the canvas is updating but not showing anything on the screen
Canvas.jsx
export class Canvas extends Component {
state = {
container: document.getElementById('test'),
canvas: document.createElement('canvas'),
ctx: document.createElement('canvas').getContext("2d"),
mouseCoords: null,
particles: [],
width: 0,
height: 0,
};
componentDidMount() {
window.scrollTo(0, 0);
document.getElementById('test').appendChild(document.createElement('canvas'));
this.setState({
container: document.getElementById('test'),
canvas: document.createElement('canvas'),
ctx: document.createElement('canvas').getContext("2d")
}, () => this.handleResize());
window.addEventListener("resize", this.handleResize)
}
r = (e, t) => {
return Math.hypot(t[0] - e[0], t[1] - e[1])
};
i = (e, t, o, s) => {
var n = e / s - 1;
return o * (n * n * (2.2 * n + 1.2) + 1) + t
};
componentDidUpdate(prevProps, prevState, snapshot) {
return null;
}
componentWillUnmount() {
this.setState({
canvas: document.getElementById('test').remove(),
});
}
handleAnimate = () => {
var e = this.state;
var that = this;
if (!(e.width < 800 * window.devicePixelRatio)) {
var t = e.particles[e.particles.length - 1]
, o = (e.width - t.coords.ref[0]) / 2
, s = t.coords.ref[0]
, n = (e.height - t.coords.ref[1]) / 2
, a = t.coords.ref[1];
e.ctx.clearRect(o, n, s, a);
e.particles.forEach((t) => {
t.timer += 1;
const o = Math.max(that.r(t.coords.ref, e.mouseCoords), 150);
s = t.coords.ref[0] + (e.mouseCoords[0] - t.coords.ref[0]) / (o / e.width * 150);
n = t.coords.ref[1] + (e.mouseCoords[1] - t.coords.ref[1]) / (o / e.height * 150);
a = 150 * t.r.ref / o + .5;
/* eslint-disable */
s === t.coords.new[0] && n === t.coords.new[1] || (t.coords.old = t.coords.current.slice(), t.coords.new = [s, n], t.r.old = t.r.current, t.r.new = a, t.timer = 1);
t.timer < 75 && (t.coords.current[0] = that.i(t.timer, t.coords.old[0], t.coords.new[0] - t.coords.old[0], 75),
t.coords.current[1] = that.i(t.timer, t.coords.old[1], t.coords.new[1] - t.coords.old[1], 75),
t.r.current = Math.max(that.i(t.timer, t.r.old, t.r.new - t.r.old, 75), 0));
e.ctx.fillStyle = t.fill;
e.ctx.beginPath();
e.ctx.arc(t.coords.current[0], t.coords.current[1], t.r.current, 0, 2 * Math.PI);
e.ctx.fill()
});
this.setState({
loop: requestAnimationFrame(this.handleAnimate)
})
}
};
handleHover = (e) => {
this.state.mouseCoords = [e.clientX * window.devicePixelRatio, e.clientY * window.devicePixelRatio]
};
handleResize = () => {
const {canvas} = this.state;
var particle = [];
let newWidth = window.innerWidth * window.devicePixelRatio,
newHeight = window.innerHeight * window.devicePixelRatio;
canvas.height = newHeight;
canvas.width = newWidth;
this.setState({
width: window.innerWidth * window.devicePixelRatio,
height: window.innerHeight * window.devicePixelRatio,
canvas,
mouseCoords: [0, newHeight]
});
var e = Math.max(newHeight / 21, 40),
t = Math.floor(newWidth / (e + 5)),
o = Math.floor(newHeight / (e + 10)) || 90,
V = [2, 2, 6, 6, 5, 5, 3, 3, 0, 0, 2, 2, 0, 0],
j = [[2, 0, 0, 2, 1, 0, 0, 3], [1, 0, 2, 2, 0, 0, 2], [2, 2, 2, 2, 3, 0, 1, 0, 0], [1, 0, 2, 2, 0, 0, 2, 2, 0], [2, 0, 1, 2, 2, 0, 2, 0, 1, 2, 2, 1, 0, 0], [0, 2, 2, 0, 3, 1, 2, 1, 2, 0, 1, 2, 2, 0], [1, 2, 1, 1, 0, 2, 2, 0, 0, 1, 2, 1, 0, 1], [2, 1, 1, 0, 0, 0, 2, 2, 1, 1, 1, 1], [2, 0, 2, 0, 0, 0], [0, 0, 0, 2, 0, 0], [0, 2, 0, 0, 0, 0, 0], [0, 0, 0, 2, 0, 0], [0, 0, 0, 0, 2, 0], [0, 0, 0, 2, 0, 0]];
console.log(t);
for (let s = 0; s < o; s += 1) {
for (var n = 0; n < t; n += 1) {
var a = Math.round(this.state.width / 2 - e / 2 * (t - 1) + n * e),
d = Math.round(this.state.height / 2 - e / 2 * (o - 1) + s * e),
l = "#555555",
r = e / 20;
if (s > 0 && s < j.length + 1)
switch (j[s - 1][t - n - V[s - 1] - 1]) {
case 0:
l = "#1DCA7F";
r = e / 2.5;
break;
case 1:
l = "#047870";
r = e / 4;
break;
case 2:
l = "#FFFFFF";
break;
case 3:
l = "#6898ae";
r = e / 2.5
}
var i = {
coords: {
ref: [a, d],
current: [a, d],
new: [a, d],
old: [a, d]
},
r: {
ref: r,
current: 0,
new: r,
old: 0
},
fill: l,
timer: 1
};
particle.push(i);
}
}
this.setState({
particles: particle
})
};
render() {
// className="-content-wrapper header"
return (
<div className="home">
<header id="test" onMouseEnter={() => this.handleAnimate()}
onMouseMove={e => this.handleHover(e)}>
</header>
</div>
)}
}
what I want to achieve is a canvas with particles.on hover the particles should move towards where the cursor is headed and zoom the greener particles

You could use refs (note that facebook discourages the uses of refs on most situations, see the documentation.
I would do something like this:
export class Canvas extends React.Component {
constructor(props) {
super(props);
this.canvas = React.createRef();
this.context = null // this will be initializaed in componentDidMount()
}
componentDidMount(){
this.context = this.canvas.getContext("2d")
}
...
render() {
return (
<div className="home">
<header id="test" onMouseEnter={() => this.handleAnimate()}
onMouseMove={e => this.handleHover(e)}>
</header>
<canvas ref={this.canvas} className={style.canvas}/>
<!-- Make sure to size the canvas correctly using CSS -->
</div>
)}
}

Related

How to resize a window/app to appear on a second monitor using Phoenix macOS window and app manager?

I have this code installed and it's working. If I'm on my second monitor and run the commands it resizes and positions the window to the main screen.
How do I set it so that if the window/app is open on the second monitor then running the key command resizes/positions the window to that second monitor and not to the main monitor? So I want to resize the window to whatever the current screen that the window is on.
I'm not a programmer at all...barely able to read the basics of the code below so I'd need very specific help here...like where to add what code/what code to replace with what...
I read the Phoenix documentation but don't understand it.
https://kasper.github.io/phoenix/api/screen
Here is the js code:
function resizeToFraction(
xNumerator,
xDenominator,
widthNumerator,
widthDenominator,
yNumerator,
yDenominator,
heightNumerator,
heightDenominator,
) {
return () => {
const window = Window.focused();
if (window) {
const screen = window.screen().flippedVisibleFrame();
const x = (screen.width / xDenominator) * xNumerator;
const y = (screen.height / yDenominator) * yNumerator;
const width = (screen.width / widthDenominator) * widthNumerator;
const height = (screen.height / heightDenominator) * heightNumerator;
window.setFrame({ x, y, width, height });
}
};
}
function resizeVerticallyInPlace(
yNumerator,
yDenominator,
heightNumerator,
heightDenominator,
) {
return () => {
const window = Window.focused();
if (window) {
const screen = window.screen().flippedVisibleFrame();
const currentFrame = window.frame();
const x = currentFrame.x;
const y = (screen.height / yDenominator) * yNumerator;
const width = currentFrame.width;
const height = (screen.height / heightDenominator) * heightNumerator;
window.setFrame({ x, y, width, height });
}
};
}
function focusClosestNeighbor(direction) {
return () => {
const window = Window.focused();
const currentApp = window.app();
const appWindows = currentApp.windows();
const neighbors = window.neighbors(direction);
const thisAppNeighbor = neighbors.find((n) => appWindows.includes(n));
if (thisAppNeighbor) {
thisAppNeighbor.focus();
} else if (neighbors.length > 0) {
const visibleNeighbor = neighbors.find((n) => n.isVisible() && n.isNormal());
if (visibleNeighbor) {
visibleNeighbor.focus();
}
}
};
}
// fourths
["o", "p", "[", "]"].forEach((key, index) => {
Key.on(key, ["ctrl", "shift", "cmd"], resizeToFraction(index, 4, 1, 4, 0, 1, 1, 1));
});
// thirds
["j", "k", "l"].forEach((key, index) =>
Key.on(key, ["ctrl", "cmd"], resizeToFraction(index, 3, 1, 3, 0, 1, 1, 1)),
);
// halves
[";", "'"].forEach((key, index) =>
Key.on(key, ["ctrl", "cmd"], resizeToFraction(index, 2, 1, 2, 0, 1, 1, 1)),
);
// two-thirds
Key.on("j", ["ctrl", "cmd", "shift"], resizeToFraction(0, 3, 2, 3, 0, 1, 1, 1));
Key.on("l", ["ctrl", "cmd", "shift"], resizeToFraction(1, 3, 2, 3, 0, 1, 1, 1));
// divide into half up/down
Key.on("k", ["ctrl", "shift", "cmd", "alt"], resizeVerticallyInPlace(0, 2, 1, 2));
Key.on("j", ["ctrl", "shift", "cmd", "alt"], resizeVerticallyInPlace(1, 2, 1, 2));
// fill
Key.on("m", ["ctrl", "cmd"], resizeToFraction(0, 1, 1, 1, 0, 1, 1, 1));
// focus
// Key.on("left", ["ctrl", "shift", "cmd"], focusClosestNeighbor("west"));
// Key.on("right", ["ctrl", "shift", "cmd"], focusClosestNeighbor("east"));
// Key.on("up", ["ctrl", "shift", "cmd"], focusClosestNeighbor("north"));
// Key.on("down", ["ctrl", "shift", "cmd"], focusClosestNeighbor("south"));

Step Property for a circular slider

Could anyone please help me in creating a stepper function that would take a parameter 'n' and would increment my slider according to n's value. Circular slider I'm using is this :
https://github.com/fseehawer/react-circular-slider
This is how the knob is being set based on different angles, but what if I want to set the knob at angles separated with 20 degree angle.
const setKnobPosition = useCallback(
(radians) => {
const radius = state.radius - trackSize / 2;
const offsetRadians = radians + knobOffset[knobPosition];
const degrees = (offsetRadians > 0 ? offsetRadians : 2 * Math.PI + offsetRadians)
* (spreadDegrees / (2 * Math.PI));
const pointsInCircle = (state.angles.length - 1) / spreadDegrees;
const currentPoint = Math.round(degrees * pointsInCircle);
if (state.angles[currentPoint] !== state.currentPoint) {
onChange(state.angles[currentPoint]);
}
dispatch({
type: 'setKnobPosition',
payload: {
displayedCompassAngle: state.angles[currentPoint],
knob: {
x: radius * Math.cos(radians) + radius,
y: radius * Math.sin(radians) + radius,
},
},
});
},
[
state.radius,
state.angles,
state.displayedCompassAngle,
knobPosition,
trackSize,
direction,
onChange,
],
);
These are initial state and different props I am using.
const CircularSlider = (props) => {
const {
width = 280,
direction = 1,
knobPosition = 'top',
hideLabelValue = false,
knobDraggable = true,
trackSize = 8,
angles = [],
compassAngle = 0,
onChange = () => {},
} = props;
const initialState = {
mounted: false,
isDragging: false,
width,
radius: width / 2,
knobPosition,
displayedCompassAngle: 0,
angles,
radians: 0,
offset: 0,
knob: {
x: 0,
y: 0,
},
};

How to avoid variable scope issues within a map function?

I have a working solution of this problem but I'm trying to make a cleaner and neat version of it as much as possible. I came up with another solution that uses a function within a map function. Unfortunately, this version has a few issues and I want to just know why the second solution is not working. I'm guessing it's a variable scope issue here. I'm looking forward to know your opinion about it.
I have a simple function that prints calendar days in an array!
So a question is why the first version of my code get the expected results while the second version prints unexpected results.
I tried to change let to var and I also made the counter and startedIndexing outside the function scope.
Solution 1 (works):
const currentFullMonth = {
days_length: 31,
first_day: "Thu",
first_day_index: 4,
last_day: "Sat",
last_day_index: 6,
month: "Aug",
year: 2019
}
const testMonth = [
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0]
];
function printMonthCalender(month) {
let counter = 0;
let startedIdxing = false;
return month.map(week => {
return week.map((day, index) => {
if (index === currentFullMonth.first_day_index && !startedIdxing) {
counter++;
startedIdxing = true;
return counter;
} else if (startedIdxing) {
if (currentFullMonth.days_length === counter) {
counter = 0;
}
counter++;
return counter;
} else {
return 0;
}
});
});
} // end of Solution #1 <-- this works :)
Solution 2 (doesn't work):
// start of Solution #2 <-- does not work :(
// im using two functions to make it look more cleaner
//
function printMonthCalender2(month) {
let counter = 0;
let startedIdxing = false;
return month.map(week => {
return week.map((day, index) =>
indexingMonth(counter, startedIdxing, index)
);
});
}
function indexingMonth(counter, startedIdxing, index) {
if (index === currentFullMonth.first_day_index && !startedIdxing) {
counter++;
startedIdxing = true;
return counter;
} else if (startedIdxing) {
if (currentFullMonth.days_length === counter) {
counter = 0;
}
counter++;
return counter;
} else {
return 0;
}
}// end of Solution #2
console.log(printMonthCalender(testMonth));
console.log(printMonthCalender2(testMonth));
expected result as follows (first version):
[0, 0, 0, 0, 1, 2, 3]
[4, 5, 6, 7, 8, 9, 10]
[11, 12, 13, 14, 15, 16, 17]
[18, 19, 20, 21, 22, 23, 24]
[25, 26, 27, 28, 29, 30, 31]
[1, 2, 3, 4, 5, 6, 7]
unexpected result as follows (second version):
[0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0]
The problem is that when you reassign startedIdxing inside of indexingMonth, it's a local variable, so it doesn't get changed inside of the calling function (printMonthCalender2).
An issue is that .map should not have mutation or reassignment as a side-effect. While you could tweak things so that indexingMonth returned something that you checked and then reassigned startedIdxing to, I'd prefer a different approach: create a flat array, eg
[0, 0, 0, 0, 1, 2, ..., 30, 31, 1, 2, 3]
and then chunk it into pieces of 7 afterwards:
const currentFullMonth = {
days_length: 31,
first_day: "Thu",
first_day_index: 4,
last_day: "Sat",
last_day_index: 6,
month: "Aug",
year: 2019
}
const makeZeroArr = length => new Array(length).fill(0);
const printMonthCalendar = (testMonth) => {
// Create array: [1, 2, 3, ..., 30, 31]
const oneMonth = Array.from(
{ length: currentFullMonth.days_length },
(_, i) => i + 1
);
// Create a flat array with leading zeros and trailing last week:
// [0, 0, 0, 0, 1, 2, 3, ..., 30, 31, 1, 2, 3, 4, 5, 6, 7]
const flatResultArr = [
...makeZeroArr(currentFullMonth.first_day_index),
...oneMonth,
...oneMonth // this includes extra numbers that will be trimmed
].slice(0, 7 * 6); // 7 days/week * 6 weeks
// Chunk the flat array into slices of 7:
const resultArr = [];
for (let i = 0; i < 7; i++) {
resultArr.push(flatResultArr.slice(i * 7, (i + 1) * 7));
}
return resultArr;
};
console.log(printMonthCalendar());
In functions, primitive types like numbers and booleans are passed by value, not by reference. So when you define counter and startedIdxing in printMonthCalender2 and then try to change them in indexingMonth, the changes get lost as soon as you return to printMonthCalender2.
However in JavaScript, objects get passed by reference. So something like this would work:
function printMonthCalender2(month) {
let obj = { counter: 0, startedIdxing = false };
return month.map(week => {
return week.map((day, index) =>
indexingMonth(obj, index)
);
});
}
function indexingMonth(obj, index) {
if (index === currentFullMonth.first_day_index && !obj.startedIdxing) {
obj.counter++;
obj.startedIdxing = true;
return obj.counter;
} else if (obj.startedIdxing) {
if (currentFullMonth.days_length === obj.counter) {
obj.counter = 0;
}
obj.counter++;
return obj.counter;
} else {
return 0;
}
}// end of Solution #2
Things like obj.counter++ will actually keep those changes in your original object defined in printMonthCalender2.
Warning: While you can do this, if you are working with complex code this is often frowned upon. These kinds of mutations can be very difficult to debug if a problem occurs. It's a legitimate programming technique, but shouldn't be abused.
Also if you're working in a team that adheres to the functional programming paradigm, I believe this is a big no-no.
However given the very short duration and limited scope of the obj variable in this example, I would personally feel very comfortable with this. If obj had a much longer lifetime and was used in numerous places in the code then I'd be more wary of it, and would agree with #CertainPerformance's comment that a map statement shouldn't mutate things.

Sum of Parts of An Array - JavaScript

Trying to solve this challenge on codewars. According to the challenge, the parts of array:
ls = [0, 1, 3, 6, 10]
Are
ls = [0, 1, 3, 6, 10]
ls = [1, 3, 6, 10]
ls = [3, 6, 10]
ls = [6, 10]
ls = [10]
ls = []
And we need to return an array with the sums of those parts.
So my code is as follows:
function partsSums(ls) {
let arrayOfSums = [];
while(ls.length > 0) {
let sum = ls.reduce((a, b) => a + b);
arrayOfSums.push(sum);
ls.shift();
}
return arrayOfSums;
}
console.log(partsSums([0, 1, 3, 6, 10]));
The issue is that it wants us to add the last sum 0 when the array is empty. So we should be getting:
[ 20, 20, 19, 16, 10, 0 ]
Instead of
[ 20, 20, 19, 16, 10]
So I tried this:
function partsSums(ls) {
let arrayOfSums = [];
while(ls.length > 0) {
let sum = ls.reduce((a, b) => a + b);
arrayOfSums.push(sum);
ls.shift();
}
arrayOfSums.push(0);
return arrayOfSums;
}
console.log(partsSums([0, 1, 3, 6, 10]));
And this:
function partsSums(ls) {
ls.push(0);
let arrayOfSums = [];
while(ls.length > 0) {
let sum = ls.reduce((a, b) => a + b);
arrayOfSums.push(sum);
ls.shift();
}
return arrayOfSums;
}
But these caused execution time-out errors on Codewars:
Execution Timed Out (12000 ms)
So I also tried:
function partsSums(ls) {
let arrayOfSums = [];
while(ls.length > -1) {
let sum = ls.reduce((a, b) => a + b);
arrayOfSums.push(sum);
ls.shift();
}
return arrayOfSums;
}
But now this causes a TypeError:
TypeError: Reduce of empty array with no initial value
I am not understanding the concept of how to get 0 into the array when all of the values have been shifted out. The challenge seems to want 0 as the final "sum" of the array, even when the array is empty. But you cannot reduce an empty array - what else can I do here?
EDIT: Tried adding initial value to the reduce method:
function partsSums(ls) {
let arrayOfSums = [];
while(ls.length > 0) {
let sum = ls.reduce((a, b) => a + b, 0);
arrayOfSums.push(sum);
ls.shift();
}
return arrayOfSums;
}
Unfortunately this still fails the basic test :
expected [] to deeply equal [ 0 ]
There is no reason to compute the sum over and over. On a long array this will be very inefficient ( O(n²) ) and might explain your timeout errors. Compute the sum at the beginning and then subtract each element from it in a loop.
ls = [0, 1, 3, 6, 10]
function partsSums(ls) {
let sum = ls.reduce((sum, n) => sum + n, 0)
res = [sum]
for (let i = 1; i <= ls.length; i++){
sum -= ls[i-1]
res.push(sum )
}
return res
}
console.log(partsSums(ls))
Another solution that passed all of the tests:
function partsSums(ls) {
let result = [0],
l = ls.length - 1;
for (let i = l; i >= 0; i--) {
result.push(ls[i] + result[ l - i]);
}
return result.reverse();
}
console.log(partsSums([]));
console.log(partsSums([0, 1, 3, 6, 10]));
console.log(partsSums([1, 2, 3, 4, 5, 6]));
console.log(partsSums([744125, 935, 407, 454, 430, 90, 144, 6710213, 889, 810, 2579358]));
You could use for loop with slice and when i == 0 you can slice len + 1 which is going to return you empty array and sum will be 0.
function partsSums(arr) {
const res = [], len = arr.length
for (let i = len; i > -1; i--) {
res.push(arr.slice(-i || len + 1).reduce((a, n) => a + n, 0))
}
return res;
}
console.log(partsSums([0, 1, 3, 6, 10]));
console.log(partsSums([1, 2, 3, 4, 5, 6]));
console.log(partsSums([744125, 935, 407, 454, 430, 90, 144, 6710213, 889, 810, 2579358]));
You can also use two double reduce and if there is no next element push zero.
function partsSums(arr) {
const sum = arr => arr.reduce((r, e) => r + e, 0);
return arr.reduce((r, e, i, a) => {
const res = sum(a.slice(i, a.length));
return r.concat(!a[i + 1] ? [res, 0] : res)
}, [])
}
console.log(partsSums([0, 1, 3, 6, 10]));
console.log(partsSums([1, 2, 3, 4, 5, 6]));
console.log(partsSums([744125, 935, 407, 454, 430, 90, 144, 6710213, 889, 810, 2579358]));
try this with recursion :
function partsSums(ls) {
let sum = ls.reduce((a, b) => a + b, 0);
return ls.length > 0 ? [sum].concat(partsSums(ls.slice(1))) : [0];
}
console.log(partsSums([0, 1, 3, 6, 10]));
console.log(partsSums([1, 2, 3, 4, 5, 6]));
console.log(partsSums([744125, 935, 407, 454, 430, 90, 144, 6710213, 889, 810, 2579358]));
Here's one thing you could do
function partsSums(ls) {
if(!ls.length) return [0];
let prevTotal = ls.reduce((a,b) => a + b);
return [prevTotal, ...ls.map(val => prevTotal -= val)]
}
console.log(partsSums([0, 1, 3, 6, 10]));
You could iterate from the end and take this value plus the last inserted value of the result set.
This approach works with a single loop and without calculating the maximum sum in advance.
function partsSums(ls) {
var result = [0],
i = ls.length;
while (i--) {
result.unshift(ls[i] + result[0]);
}
return result;
}
console.log(partsSums([0, 1, 3, 6, 10]));
console.log(partsSums([]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
With push and reverse.
function partsSums(ls) {
var result = [0],
l = 0,
i = ls.length;
while (i--) result.push(l += ls[i]);
return result.reverse();
}
console.log(partsSums([0, 1, 3, 6, 10]));
console.log(partsSums([]));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Calculating quarterly and yearly avarage through javascript

I have an array:
const test = [1,2,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
I want to group the elements of the array into chunks of size 3 (quarters) and size 12 (years):
const quarters = [[1,2,2],[4,5,6],[7,8,9],[10,11,12],[13,14,15],[16,17,18],[19,20]];
const years = [[1,2,2,4,5,6,7,8,9,10,11,12],[13,14,15,16,17,18,19,20]];
I also want to compute the sum of each chunk:
const quarterSums = [5,15,24,33,42,51,39];
const yearSums = [77,132];
How do I do so?
Use a loop that increments by the group size, and use .slice().
EDIT: You added information not in the original question. Since you seem to want the sum of each quarter/year, add this .reduce((s,n)=>s+n, 0) to each subset. This shows a better use of .reduce().
const test = [1, 2, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
console.log(getGroups(test, 3)); // quarters
console.log(getGroups(test, 12)); // years
function getGroups(a, s) {
for (var i = 0, r = []; i < a.length; i += s) {
r.push(a.slice(i, i + s).reduce((s,n)=>s + n, 0));
}
return r;
}
Using something like .reduce() that visits every element makes it more complicated in this case. The traditional for loop provides the benefit of defining how the loop should be incremented.
If you prefer a more function way, I'd still not use .reduce(), but would roll my own tail recursion.
const test = [1, 2, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
console.log(getGroups(test, 3)); // quarters
console.log(getGroups(test, 12)); // years
function getGroups(a, s) {
return function p(a, s, r) {
return !a.length ? r : r.concat(a.slice(0, s).reduce((s,n)=>s + n, 0),
p(a.slice(s), s, r));
}(a, s, []);
}
If you want to group elements into chunks of size n then:
const groupInto = (n, xs) => xs.reduce((xss, x, i) => {
if (i % n === 0) xss.push([]); // create a new group
xss[xss.length - 1].push(x); // push in last group
return xss;
}, []);
const xs = [1,2,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
const quarters = groupInto(3, xs);
const years = groupInto(12, xs);
console.log(JSON.stringify(quarters));
console.log(JSON.stringify(years));
On the other hand, if you want to find the sum of these chunks:
const sumInto = (n, xs) => xs.reduce((ys, x, i) => {
if (i % n === 0) ys.push(0);
ys[ys.length - 1] += x;
return ys;
}, []);
const xs = [1,2,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
const quarters = sumInto(3, xs);
const years = sumInto(12, xs);
console.log(JSON.stringify(quarters));
console.log(JSON.stringify(years));
Hope that helps.
You could use a Array#forEach with an object as temporary variable for collecting the values. Then calculate the average.
var values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24],
temp = { quarter: { avg: [], items: 3, sum: 0 }, year: { avg: [], items: 12, sum: 0 } }
values.forEach(function (v, i) {
Object.keys(temp).forEach(function (k) {
temp[k].sum += v;
if (i && (i + 1) % temp[k].items === 0) {
temp[k].avg.push(temp[k].sum / temp[k].items);
temp[k].sum = 0;
}
});
});
console.log(temp.quarter.avg);
console.log(temp.year.avg);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories