How can I apply a recusive function here with Javascript? - javascript

I want to fill the middle area with recursion. What is the best way to do this? I want a solution with recursion not other. How can I do that the left right top and bottom are checked?
'use strict'
let bitmap = [
'.....******.....', //.....******.....
'....*......*....', //....*++++++*....
'...*........*...', //...*++++++++*....
'....*......*....', //....*++++++*....
'.....******.....', //.....******.....
];
bitmap = bitmap.map((row) => [...row]);
const showOnPosition = (x, y) => bitmap[y][x];
//console.log(showOnPosition(4, 1));
const changeSymbol = (x, y) => (bitmap[y][x] = '+');
//console.log(changeSymbol(6, 1));
const fill = (x, y) => {
// painted?
if (showOnPosition(x, y) === '*') {
return bitmap.map((row) => row.join('')).join('\n');
} else {
// paint!
changeSymbol(x, y);
return bitmap.map((row) => row.join('')).join('\n');
}
};
console.log(fill(6, 1));
I want to check the surrounding elements and if they are not a . they should be replaced by a +. Similar to a floodFill or here only by recursion.
This is how it should look like:
.....******.....
....*++++++*....
...*++++++++*....
....*++++++*....
.....******.....

To fill recursively, you'll have to iterate over the array of one-cell differences - that is, over
[[0, 1], [0, -1], [1, 0], [-1, 0]]
From those, identify the new X and Y, and call fill again if that coordinate hasn't been filled before. Create a Set containing filled coordinates and pass it along.
Since [6] doesn't exist on the outer array, it sounds like you also need to switch around the X and Y coordinates in the parameters. (fill(6, 1) should refer to bitmap[1][6])
'use strict'
const bitmap = [
'.....******.....', //.....******.....
'....*......*....', //....*++++++*....
'...*........*...', //...*++++++++*....
'....*......*....', //....*++++++*....
'.....******.....', //.....******.....
]
.map(row => [...row]);
const fill = (y, x, filled = new Set()) => {
if (bitmap[x][y] === '.') {
// paint!
bitmap[x][y] = '+';
// recurse
for (const [xDiff, yDiff] of [[0, 1], [0, -1], [1, 0], [-1, 0]]) {
const newX = x + xDiff;
const newY = y + yDiff;
const key = `${newX}_${newY}`;
if (!filled.has(key) && bitmap[newX]) {
filled.add(key);
fill(newY, newX, filled);
}
}
}
return bitmap.map((row) => row.join('')).join('\n');
};
console.log(fill(6, 1));

Related

Multidimensional array replace with close range Javascript

Let's say we have a List of ranges expressed in arrays with two elements [from, to].
When we add a new array range like [5,8], it should check in List if there is a closest range and then replace it with the new range value. An example is provided below:
Example 1
var List = [[1,2], [3,4], [6,7], [9,10]]
var newData = [5,8]
Expected Output:
[[1,2], [3,4], [5,8], [9,10]]
The [6,7] range is already included in [5,8]
Example 2
var List = [[1,3], [4,6], [8,10]]
var newData = [5,9]
Expected Output:
[[1,3], [4,10]]
Assuming the initial list is well-formed, with its pairs sorted and non-overlapping, you could use binary search to find the end points of a new pair in the array and so determine any overlap. If overlap, splice the array accordingly:
function addSegments(segments, ...pairs) {
for (let pair of pairs) {
let [start, end] = pair.map(function (x, i) { // Binary search
let low = 0,
high = segments.length;
side = 1 - i;
while (low < high) {
let mid = (low + high) >> 1;
if (x < segments[mid][side]) high = mid;
else low = mid + 1;
}
return low - (side && segments[low-1]?.[side] === x);
});
if (start < end) {
pair = [
Math.min(segments[start][0], pair[0]),
Math.max(segments[end-1][1], pair[1])
];
}
segments.splice(start, end - start, pair);
}
}
// Demo
let list = [[1, 2], [3, 4], [6, 7], [9, 10]];
addSegments(list, [5, 8]);
console.log(JSON.stringify(list));
list = [[1, 3], [4, 6], [8, 10]];
addSegments(list, [5, 9]);
console.log(JSON.stringify(list));
If we have two arrays containing all the integers included in the ranges, then we can intersect and see if they overlap. If they do, we create a new range from the union of the two ranges, and use that in the output instead.
So to that end, we define three helper functions, range(), intersect() and union(), and then we use those to generate the output array. If the intersection exists, we overwrite any overlapped range with the new union of the two. I assumed that if two ranges just touched they weren't meant to be combined, so the overwrite is only triggered if the intersection contains more than one element. Also, I added an initial sort.
function add(list, data) {
let buffer = [], idx;
const range=(a,b)=>Array.from({length:b-a+1}, (_, i)=>(a+i));
const intersect=(a,b)=>a.filter(x=>b.includes(x));
const union=(a,b)=>[...new Set([...a, ...b])].sort((a,b)=>a-b);
list.sort((a,b)=>a[0]-b[0]);
list.forEach(el=>{
let x = range(el[0], el[1]);
let y = range(data[0], data[1]);
let i = intersect(x, y);
if(i.length>1) {
let d = union(x,y);
data = [d[0], d[d.length-1]];
if(idx) { buffer[idx] = data; }
else { idx = buffer.push(data)-1; }
}
else { buffer.push(el); };
});
return buffer;
}
// DEMO
let List = [[1,2], [3,4], [6,7], [9,10]];
let newData = [5,8];
console.log(JSON.stringify(List));
console.log(JSON.stringify(newData));
console.log(JSON.stringify(add(List, newData)));
console.log('');
List = [[1,3], [4,6], [8,10]];
newData = [5,9];
console.log(JSON.stringify(List));
console.log(JSON.stringify(newData));
console.log(JSON.stringify(add(List, newData)));
console.log('');
// DEMO WITH UNORDERED ELEMENTS
List = [[3,4], [9,10], [6,7], [1,2]];
newData = [5,8];
console.log(JSON.stringify(List));
console.log(JSON.stringify(newData));
console.log(JSON.stringify(add(List, newData)));
console.log('');

Uncaught TypeError: Cannot read property 'classList/forEach' of undefined solution?

I'm fairly new to coding and am trying to create a Tetris game. However, I've run into a few errors where I don't know the solution. Within the javascript I'm getting the error Uncaught TypeError: Cannot read property 'classList/forEach' of undefined in two separate sections.
The error for "forEach" appears at "//draw/undraw tetros" and the error for classList appears at the bottom of the code at classList.add('tetromino').
If anyone knows how I can fix this, it would be much appreciated. :)
document.addEventListener('DOMContentLoaded', () => {
document.querySelector('.grid');
let squares = Array.from(document.querySelectorAll('.grid div'));
const ScoreDisplay = document.querySelector('#score');
const StartBtn = document.querySelector('#start-button');
const width = 10;
let nextRandom = 0;
// The Tetrominoes//
const lTetromino = [
[1, width+1, width*2+1, 2],
[width, width+1, width+2, width*2+2],
[1, width+1, width*2+1, width*2],
[width, width*2, width*2+1, width*2+2]
]
const zTetromino = [
[0, width, width+1, width*2+1],
[width+1, width+2, width*2, width*2+1],
[0, width, width+1, width*2+1],
[width+1, width+2, width*2, width*2+1]
]
const tTetromino = [
[1, width, width+1, width+2],
[1, width+1, width+2, width*2+1],
[width, width+1, width+2, width*2+1],
[1, width+1, width*2+1]
]
const oTetromino = [
[0, 1, width, width+1],
[0, 1, width, width+1],
[0, 1, width, width+1],
[0, 1, width, width+1]
]
const iTetromino = [
[1, width+1, width*2+1, width*3+1],
[width, width+1, width+2, width+3],
[1, width+1, width*2+1, width*3+1],
[width, width+1, width+2, width+3]
]
const theTetrominoes = [lTetromino, zTetromino, tTetromino, oTetromino, iTetromino]
let currentPosition = 4
let currentRotation = 0
//randomly select a tetromino and its first rotation
let random = Math.floor(Math.random()*theTetrominoes.length)
let current = theTetrominoes[random][0]
// draw tetromino
function draw() {
current.forEach(index => {
squares[currentPosition + index].classList.add('tetromino')
})
}
//undraw the Tetrominoe
function undraw() {
current.forEach(index => {
squares[currentPosition + index].classList.remove('tetromino')
})
}
//make the tetromino move down every second
timerId = setInterval(moveDown, 1000)
//assign funtion to keycodes
function control(e) {
if(e.keyCode === 37) {
moveLeft()
} else if (e.keyCode === 38) {
rotate()
} else if (e.keyCode === 39) {
moveRight()
} else if (e.keyCode === 40) {
moveDown()
}
}
document.addEventListener('keyup', control)
//move down function
function moveDown() {
undraw()
currentPosition += width
draw()
freeze()
}
//freeze
function freeze() {
if(current.some(index => squares[currentPosition + index + width].classList
.contains('taken'))) {
current.forEach(index => squares[currentPosition + index].classList.add('taken'))
//start a new tetro falling
random = nextRandom
nextRandom = Math.floor(Math.random() * theTetrominoes.length)
current = theTetrominoes[random][currentRotation]
currentPosition = 4
draw()
displayShape()
}
}
//move the tetro left, unless is at the edge or there is a blockage
function moveLeft() {
undraw()
const isAtLeftEdge = current.some(index => (currentPosition + index) % width === 0)
if(!isAtLeftEdge) currentPosition -=1
if(current.some(index => squares[currentPosition + index].classList.contains('taken'))) {
currentPosition +=1
}
draw()
}
//move the tetro right, unless is at the edge or there is a blockage
function moveRight() {
undraw()
const isAtRightEdge = current.some(index => (currentPosition + index) % width
=== width -1)
if(!isAtRightEdge) currentPosition +=1
if(current.some(index => squares[currentPosition + index].classList.contains('taken'))) {
currentPosition -=1
}
draw()
}
//rotate retro
function rotate() {
undraw()
currentRotation ++
if(currentRotation === current.length) { //if the current rotation gets to 4, make it go back to 0
currentRotation = 0
}
current = theTetrominoes[random][currentRotation]
draw()
}
//show up-next tetro in mini-grid
const displaySquares = document.querySelectorAll('mini-grid div')
const displayWidth = 4
let displayIndex = 0
// the tetro without currentRotation
const upNextTetrominoes = [
[1, displayWidth+1, displayWidth*2+1, 2], //lTetromino
[0, displayWidth, displayWidth+1, displayWidth*2+1], //zTetromino
[1, displayWidth, displayWidth+1, displayWidth+2], //tTetromino
[0, 1, displayWidth, displayWidth+1], //oTetromino
[1, displayWidth+1, displayWidth*2+1, displayWidth*3+1] //iTetromino
]
//display the shape in the mini-grid display
function displayShape() {
//remove any trace of a tetromino form the entire grid
displaySquares.forEach(square => {
square.classList.remove('tetromino')
})
upNextTetrominoes[nextRandom].forEach( index => {
displaySquares[displayIndex + index].classList.add('tetromino')
})
}
})
Please provide minimal reproducible example. However with the code provided following seem the problems
Problem 1
The error for "forEach" appears at "//draw/undraw tetros"
This is because you are nesting arrays at 3 level but trying to get like this let current = theTetrominoes[random][0] which means you are trying to access 2d array which it's not.
Solution
Try to make it 2d array or access it like 3d array:
theTetrominoes[random][0][desired_index]
Problem 2
error for classList appears at the bottom of the code at classList.add('tetromino')
You are passing index as the first parameter to forEach() which in fact is an actual item stored.
Solution
You should do something like this:
current.forEach((item, index) => {
console.log(index)
});

Storing variables' names in an array

I have four variables and I want to change their values using a function where I just can put in an array storing the variables. I'm making a game which uses a coordinate system and therefore I have four coordinates that I want to constantly update with y-axis and x-axis. I have one array, yAxis, with all y values and one array, xAxis, with all x values. I want to combine them into the coordinates. Of course, I can update them using the following code:
yAxis = [10, 10, 9, 9];
xAxis = [4, 4, 5, 5];
coordinate1 = "" + yAxis[0] + xAxis[0];
coordinate2 = "" + yAxis[1] + xAxis[1];
coordinate3 = "" + yAxis[2] + xAxis[2];
coordinate4 = "" + yAxis[3] + xAxis[3];
But instead of changing their values like earlier I would like to do something like this: This function will take the array below, coordinatesArray as a, yAxis as b, and xAxis as c. Then x is just an integer.
test(a, b, c){
for(x = 0; x < 4; x++){
a[x] = "" + b[x] + c[x];
}
}
and then I would call this function like this:
coordinatesArray = [coordinate1, coordinate2, coordinate3, coordinate4];
test(coordinatesArray, yAxis, xAxis);
What it then should do with whatever array I run the test function with:
coordinatesArray[0] = "" + yAxis[0] + xAxis[0];
coordinatesArray[1] = "" + yAxis[1] + xAxis[1];
coordinatesArray[2] = "" + yAxis[2] + xAxis[2];
coordinatesArray[3] = "" + yAxis[3] + xAxis[3];
And for example coordinatesArray[0] should then represent coordinate1.
So I would create an array to store the variables so I can easily change which variable to target. The problem though, when I run this, a[x] isn't the variable name, instead, it is their values which means this doesn't work. So my question is, is there any way to store the variables' names in an array so I can target them using a function similar to the one I showed? I want to store the names of the variables in an array and then be able to use the name to target the variable so I can change their values.
Arrays in Javascript has only indices not names, that's why you need Object:
yAxis = [10, 10, 9, 9];
xAxis = [4, 4, 5, 5];
coordinatesArray = ['coordinate1', 'coordinate2', 'coordinate3', 'coordinate4'];
function test(a, b, c){
let arrOfObj = [];
for(let i=0; i < a.length; i++){
let obj = {};
obj[a[i]] = [b[i], c[i]];
arrOfObj.push(obj);
}
return arrOfObj;
}
console.log(test(coordinatesArray,yAxis,xAxis));
So this is what you're trying to do...
yAxis = [10, 10, 9, 9];
xAxis = [4, 4, 5, 5];
coordinate1 = "magic";
coordinate2 = "will";
coordinate3 = "happen";
coordinate4 = "here";
function test(a, b, c) {
for(x = 0; x < 4; x++){
window[a[x]] = "" + b[x] + c[x];
}
}
function log(a) {
let output = [];
for(x = 0; x < 4; x++){
output.push(window[a[x]]);
}
console.log(output);
};
coordinatesArray = ["coordinate1", "coordinate2", "coordinate3", "coordinate4"];
log(coordinatesArray);
test(coordinatesArray, xAxis, yAxis);
log(coordinatesArray);
...but the comments at your question are trying to say that you don't need the variables coordinate1, coordinate2 etc, you can just ALWAYS use one array, like this:
yAxis = [10, 10, 9, 9];
xAxis = [4, 4, 5, 5];
coordinatesArray = ["magic", "will", "happen", "here"];
function test(a, b, c) {
for(x = 0; x < 4; x++){
a[x] = "" + b[x] + c[x];
}
}
console.log(coordinatesArray);
test(coordinatesArray, xAxis, yAxis);
console.log(coordinatesArray);
right?
To me it looks like the OP asks for a simple object based key value store. But I might have gotten this wrong.
Thus the answer first puts the focus on how to generate a list of concatenated pairs of x-y coordinates, which in my opinion is the most pragmatic approach ...
const yAxis = [10, 10, 9, 9];
const xAxis = [4, 4, 5, 5];
const coordinatesList = yAxis.map((y, idx) => `${ y }${ xAxis[idx] }`);
console.log('(...coordinatesList) =>', coordinatesList);
.as-console-wrapper { max-height: 100%!important; top: 0; }
If one is not in need of assigning the list's coordinate values, each to a variable-name that could not already be generically created, this list already is a good enough storage.
On the other hand, a destructuring assignment of such a list allows the creation of tailored variables exactly within the scope that is needed (the example code of this approach does also refactor the mapping function from the first provided example) ...
function concatinateCorrespondingBoundArrayItem(partial, idx) {
return [partial, this[idx]].join('');
}
const yAxis = [10, 10, 9, 9];
const xAxis = [4, 4, 5, 5];
const [
foo,
bar,
baz,
biz
] = yAxis.map(concatinateCorrespondingBoundArrayItem, xAxis);
console.log('foo bar baz biz :', foo, bar, baz, biz);
.as-console-wrapper { max-height: 100%!important; top: 0; }
Finally, the storage the OP might have asked for, has to be object/map based. A possible approach could then look similar to this last provided example ...
function createCoordinateRecord(collector, coordinateName, idx) {
collector.storage[coordinateName] = [
collector.yCoordList[idx],
collector.xCoordList[idx]
].join('');
return collector;
}
const yAxis = [10, 10, 9, 9];
const xAxis = [4, 4, 5, 5];
const coordinateNameList = ['foo', 'bar', 'baz', 'biz']
const coordinatesMap = coordinateNameList.reduce(createCoordinateRecord, {
xCoordList: xAxis,
yCoordList: yAxis,
storage: {}
}).storage;
console.log('coordinatesMap :', coordinatesMap);
console.log('coordinatesMap.foo :', coordinatesMap.foo);
console.log('coordinatesMap.bar :', coordinatesMap.bar);
console.log('coordinatesMap[coordinateNameList[2]] :', coordinatesMap[coordinateNameList[2]]);
console.log('coordinatesMap[coordinateNameList[3]] :', coordinatesMap[coordinateNameList[3]]);
console.log('Object.keys(coordinatesMap) :', Object.keys(coordinatesMap));
console.log('Object.values(coordinatesMap) :', Object.values(coordinatesMap));
console.log('Object.entries(coordinatesMap) :', Object.entries(coordinatesMap));
.as-console-wrapper { max-height: 100%!important; top: 0; }

What is the proper/idiomatic way to filter or map based on surrounding context in functional programming?

In functional programming, filtering based on the characteristics of a single item is relatively straightforward -- e.g., filtering to find only odd numbers:
const arrayOfInfo = [1,2,3,4,5,6,8,10,11,13,15,16,17,19]
const onlyOddNumbers = arrayOfInfo.filter(function(item) {
return (item % 2 == 1) ? true : false
})
However, I'm not sure what the idiomatic way of doing things is if I need context -- in other words, knowing something about the surrounding items. For example, if I wanted to filter for only the items that were surrounded by odd numbers on either side, I could do this (I'm taking advantage of some JavaScript characteristics and not even bothering to check whether the indexes exist first):
const surroundedByOneOddNumber = arrayOfInfo.filter(function(item,index) {
const itemBefore = arrayOfInfo[index - 1]
const itemAfter = arrayOfInfo[index + 1]
return ((itemBefore % 2 == 1) && (itemAfter % 2 == 1)) ? true : false
})
This becomes more obvious as a problematic or inefficient way to write code if I wanted to find numbers surrounded by two odd numbers on each side:
const surroundedByTwoOddNumbers = arrayOfInfo.filter(function(item,index) {
const itemBefore = arrayOfInfo[index - 1]
const itemTwoBefore = arrayOfInfo[index - 2]
const itemAfter = arrayOfInfo[index + 1]
const itemTwoAfter = arrayOfInfo[index + 2]
return ((itemBefore % 2 == 1) && (itemTwoBefore % 2 == 1) && (itemAfter % 2 == 1) && (itemTwoAfter % 2 == 1)) ? true : false
})
Obviously, if I wanted to do something like find only numbers surrounded by 50 odd numbers on each side, this would be completely pointless to write code like this.
Is there a good way to address this with functional programming, or is this a case where it is better to drop down to for/while loop-style?
CodePen to play with the samples: https://codepen.io/jnpdx/pen/MvradM
You could use a closure over the count of the left and right needed odd values, then take the left and right values and check every element and return the result of the check for filtering.
A special case is the count of zero, there you need to check just the actual element.
var array = [1, 2, 3, 4, 5, 6, 8, 10, 11, 13, 15, 16, 17, 19],
odd = item => item % 2,
getOdds = count => (a, i, aa) => {
var temp = aa.slice(i - count, i).concat(aa.slice(i + 1, i + 1 + count));
return count
? temp.length === 2 * count && temp.every(odd)
: odd(a);
};
console.log(array.filter(getOdds(0)));
console.log(array.filter(getOdds(1)));
console.log(array.filter(getOdds(2)));
.as-console-wrapper { max-height: 100% !important; top: 0; }
A smarter approach is to count contiguous odd parts and use the array for filtering.
Check if 16 is surrounded by two odd numbers
name values comment
----- --------------------------------------------------------- --------------------
array [ 1, 2, 3, 4, 5, 6, 8, 10, 11, 13, 15, 16, 17, 19]
left [ 1, 0, 1, 0, 1, 0, 0, 0, 1, 2, 3, 0, 1, 2]
right [ 1, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 2, 1]
16 item to check
3 left count >= 2
2 right count >= 2
true result for filtering
var array = [1, 2, 3, 4, 5, 6, 8, 10, 11, 13, 15, 16, 17, 19],
odd = item => item % 2,
left = array.reduce((r, a, i) => (r[i] = odd(a) ? (r[i - 1] || 0) + 1 : 0, r), []),
right = array.reduceRight((r, a, i) => (r[i] = odd(a) ? (r[i + 1] || 0) + 1 : 0, r), []),
getOdds = count => (a, i) => count
? left[i - 1] >= count && right[i + 1] >= count
: odd(a);
console.log(array.filter(getOdds(0)));
console.log(array.filter(getOdds(1)));
console.log(array.filter(getOdds(2)));
.as-console-wrapper { max-height: 100% !important; top: 0; }
The whole idea of functional programming is to write pure functions without side-effects
Array.filter is functional because it returns a new array, without mutating the original one. You can run that method on the same array millions of times without changing it.
If your logic gets complex, the code gets complex too, there is no functional magic which solves your domain problems.
However, you could make a createFilter function, which would create your filter function based on your domain requirements like:
const createFilter = ({
before = e => true,
after = e => true
}) => (entry, idx, entries) =>
before(entries[idx - 1]) && after(entries[idx + 1])
};
}
// This will return [ 4, 4 ] I guess ;)
[1, 3, 3, 3, 2, 4, 5, 2, 4, 7].filter(createFilter({
before: (e) => e % 2 === 0,
after: (e) => e % 2 === 1,
}))
The same way you could get only values where the item before is 50 and after 100:
[50, 1, 100, 4, 50, 3, 100].filter(createFilter({
before: (e) => e === 50,
after: (e) => e === 100
})) // pretty sure the output is [1, 3]
So this way you have a reusable filterCreator, extend it to your needs ;)
Update
#Aadit M Shah yes, after reading the OP again, I came to the conclusion, that my method would still work, you just have to write your own filterCreator function. And there is nothing wrong with the Array.filter actually.
const filterBySurrounding = (n, meetCondition) => {
return (item, idx, array) => {
return n <= idx && idx + n <= array.length - 1
? array.slice(idx - n, idx).every(meetCondition) &&
array.slice(idx + 1, idx + 1 + n).every(meetCondition)
: false
}
}
const isOdd = n => n % 2 === 1
array.filter(filterBySurrounding(50, isOdd))
Here's what I'd do:
const zipperFilter = (p, xs) => {
const before = []; // elements before x
const after = [...xs]; // x followed by elements after x, shallow copy
const result = [];
while (after.length > 0) {
const x = after.shift(); // remove x; thus, after = elements after x
if (p(before, x, after)) result.push(x);
before.unshift(x); // before = x followed by elements before x
}
return result;
};
const isOdd = n => n % 2 === 1;
const surroundedByPossiblyNOddNumbers = n => (before, x, after) =>
before.slice(0, n).every(isOdd) &&
after.slice(0, n).every(isOdd);
const surroundedByStrictlyNOddNumbers = n => (before, x, after) =>
before.length >= n &&
after.length >= n &&
before.slice(0, n).every(isOdd) &&
after.slice(0, n).every(isOdd);
const xs = [1,2,3,4,5,6,8,10,11,13,15,16,17,19];
const ys = zipperFilter(surroundedByPossiblyNOddNumbers(1), xs);
const zs = zipperFilter(surroundedByPossiblyNOddNumbers(2), xs);
const as = zipperFilter(surroundedByStrictlyNOddNumbers(1), xs);
const bs = zipperFilter(surroundedByStrictlyNOddNumbers(2), xs);
console.log(JSON.stringify(ys));
console.log(JSON.stringify(zs));
console.log(JSON.stringify(as));
console.log(JSON.stringify(bs));
What is a zipperFilter? It's a context sensitive list filter function based on the zipper data structure for lists. Any time you want to do context sensitive data processing (e.g. image processing) think of zippers.
The advantages of creating a custom zipperFilter function are:
It's more efficient than using the native filter method. This is because we don't have to keep using slice to generate the before and after arrays. We keep a running copy of both and update them on every iteration.
The before array is maintained in the reverse order. Hence, lower indices always correspond to closer neighbors. This allows us to simply slice the number of closest neighbors we want.
It's readable, generic and informs the reader that the filtering is context sensitive.
Hope that helps.

Convert an array into cumulative% array

I have an array like this one:
[30, 10, 4, 3, 3]
And I need to transform this to a cumulative% array in such a way that each value is the sum of all values upto there divided by sum of all values in the array.
In this case, the total is 50. So, the first value will be 30/50, i.e., 0.6 or 60%. The 2nd value will be (30+10)/50, i.e., 0.8 or 80% and so on.
The final array in this will be:
[60%, 80%, 88%, 94%, 100%]
How can I do this transformation with JavaScript? What would be the most efficient way to do so?
Try,
var x = [30, 10, 4, 3, 3];
var y = x.reduce(function(a,b){ return a+b; }, 0)
x = x.map(function(itm, index){
for(var i=0;i<index;i++) itm += x[i];
return (itm/y * 100);
});
x; //[60, 80, 88, 94, 100]
And this will look more elegant,
var x = [30, 10, 4, 3, 3];
var y = x.reduce(function(a,b){ return a+b; }, 0), sum = 0;
x = x.map(function(itm) { return sum += itm, (sum / y) * 100; });
Try
let nums = [30, 10, 4, 3, 3];
let sum = nums.reduce((prev,curr) => prev + curr);
let result = nums.map((num,i) => Math.round(nums.slice(0,i + 1).reduce((prev,curr) => prev + curr) / sum * 100) + '%');
You first need to compute the sum to get the denominator (50).
Then you need to apply the treatment for each element (new_value=value/denominator)
To do so you can use simple for loops, or even cleaner with arrays specific functions, to avoid any error:
var array = [30, 10, 4, 3, 3];
// Compute the denominator
var sum = array.reduce(function(pv, cv) { return pv + cv; }, 0);
// Duplicate the array
var arrayResult = array;
// Apply the function to each given "myArray[ind]" element
arrayResult.forEach( function(cv,ind,myArray) {
myArray[ind] = cv/sum;
// Add the previous accumulator if any
if( ind>0 ) {myArray[ind] += myArray[ind-1];}
} );
See it running on JSFiddle
edited to add the accumulator
Combine two maps, one to create an array with cumulative sums, the second to divide by the sum.
function cumulative_ratio(array) {
var x = 0;
return array .
map(v => x += v) .
map(v => v / x);
}

Categories

Resources