Related
Is there an easy way of finding the neighbours (that is, the eight elements around an element) of an element in a two-dimensional array? Short of just subtracting and adding to the index in different combinations, like this:
array[i-1][i]
array[i-1][i-1]
array[i][i-1]
array[i+1][i]
... And so on.
(pseudo-code)
row_limit = count(array);
if(row_limit > 0){
column_limit = count(array[0]);
for(x = max(0, i-1); x <= min(i+1, row_limit); x++){
for(y = max(0, j-1); y <= min(j+1, column_limit); y++){
if(x != i || y != j){
print array[x][y];
}
}
}
}
Of course, that takes almost as many lines as the original hard-coded solution, but with this one you can extend the "neighborhood" as much as you can (2-3 or more cells away)
I think Ben is correct in his approach, though I might reorder it, to possibly improve locality.
array[i-1][j-1]
array[i-1][j]
array[i-1][j+1]
array[i][j-1]
array[i][j+1]
array[i+1][j-1]
array[i+1][j]
array[i+1][j+1]
One trick to avoid bounds checking issues, is to make the array dimensions 2 larger than needed. So, a little matrix like this
3 1 4
1 5 9
2 6 5
is actually implemented as
0 0 0 0 0
0 3 1 4 0
0 1 5 9 0
0 2 6 5 0
0 0 0 0 0
then while summing, I can subscript from 1 to 3 in both dimensions, and the array references above are guaranteed to be valid, and have no effect on the final sum.
I am assuming c, and zero based subscripts for the example
Here is a working Javascript example from #seb original pseudo code:
function findingNeighbors(myArray, i, j) {
var rowLimit = myArray.length-1;
var columnLimit = myArray[0].length-1;
for(var x = Math.max(0, i-1); x <= Math.min(i+1, rowLimit); x++) {
for(var y = Math.max(0, j-1); y <= Math.min(j+1, columnLimit); y++) {
if(x !== i || y !== j) {
console.log(myArray[x][y]);
}
}
}
}
an alternative to #SebaGR, if your language supports this:
var deltas = { {x=-1, y=-1}, {x=0, y=-1}, {x=1, y=-1},
{x=-1, y=0}, {x=1, y=0},
{x=-1, y=1}, {x=0, y=1}, {x=1, y=1} };
foreach (var delta in deltas)
{
if (x+delta.x < 0 || x + delta.x >= array.GetLength(0) ||
y+delta.y < 0 || y + delta.y >= array.GetLength(1))
continue;
Console.WriteLine("{0}", array[x + delta.x, y + delta.y]);
}
Slight advantage in readability, possible performance if you can statically allocate the deltas.
To print the neighbors of L[row][column]:
print(L[row-1][column-1], L[row-1][column], L[row-1][column+1])
print(L[row][column-1], L[row][column], L[row][column+1])
print(L[row+1][column-1], L[row+1][column], L[row+1][column+1])
That's probably the fastest/easiest way is to just print possible neighbors. Make sure to do index out of bound checking though.
Some languages might offer a shortcut way of doing this, but I don't know of any.
This is an implementation of #Seb's answer in python3+ that is concise and uses generators for max performance:
def neighbours(pos, matrix):
rows = len(matrix)
cols = len(matrix[0]) if rows else 0
for i in range(max(0, pos[0] - 1), min(rows, pos[0] + 2)):
for j in range(max(0, pos[1] - 1), min(cols, pos[1] + 2)):
if (i, j) != pos:
yield matrix[i][j]
Grid (vector 2D or one dimension... not the problem here)
X & Y, coordinate of your element (or just pass your vector element by ref...)
int neighbour(const Grid & g, const size_t & x, const size_t & y) {
for (int i = -1; i < 2; ++i)
for (int j = -1; j < 2; ++j)
if (x + i >= 0 && x + i < g.row && y + j >= 0 && y + j < g.col)
//Do some stuff
return 0;
}
// My approach in JS
let size = 10
//or some arbitrary number for the size of your grid.
const neighbors = [
[-1, -1],
[-1, 0],
[-1, 1],
[0, -1],
[0, 1],
[1, -1],
[1, 0],
[1, 1]
]
for (let i = 0; i < size; i++) {
for (let j = 0; j < size; j++) {
neighbors.forEach(([x, y]) => {
const newI = i + x;
const newJ = j + y;
if (
newI >= 0 &&
newI < size &&
newJ >= 0 &&
newJ < size
) {
// you can access your grid neighbors here ----> grid[newI][newJ];
}
```
I've found this approach helpful because it defines all of the array coordinates as transformations of the existing i and j indexes in your for loops.
Here is a convenient method in Python:
def neighbors(array,pos):
n = []
string = "array[pos.y+%s][pos.x+%s]"
for i in range(-1,2):
for j in range(-1,2):
n.append(eval(string % (i,j)))
return n
Assuming pos is some 2D Point object and array is a 2D array.
Since in a matrix around an element there are only 8 elements, you can use array to store different index values.For e.g.,
int iarr[8] = {-1,-1,-1,0,0,+1,+1,+1};
int jarr[8] = {-1,0,+1,-1,+1,-1,0,+1};
for(int i = 0 ; i < 8 ; i++)
{
if(arr[x-iarr[i]][y-jarr[i]] == 1)
{
//statements
}
}
/* x and y are the position of elements from where you want to reach out its neighbour */
since both array contains just 8 values , then space might not be a problem.
The approach I usually take is described on the bottom of this blog:
https://royvanrijn.com/blog/2019/01/longest-path/
Instead of hardcoding the directions or having two nested loops I like to use a single integer loop for the 8 ‘directions’ and use (i % 3)-1 and (i / 3)-1; do check out the blog with images.
It doesn’t nest as deep and is easily written, not a lot of code needed!
JS sample :
function findingNeighbors(myArray, i, j){
return myArray.reduce(function(a, b, c){
if(Math.max(0, i-1) <= c && c <= Math.min(i+1, myArray.length-1)){
a = a.concat(
b.reduce(function(d, e, f){
if(f == j && c == i)
return d;
if(Math.max(0, j-1) <= f && f <= Math.min(j+1, myArray.length-1))
d.push(e)
return d;
},[])
);
}
return a;
},[]);
}
A lot depends on what your data is. For example, if your 2D array is a logical matrix, you could convert rows to integers and use bitwise operations to find the ones you want.
For a more general-purpose solution I think you're stuck with indexing, like SebaGR's solution.
Rows and Cols are total number of rows and cols
Define a CellIndex struct or class. Or you can just return the actual values instead of the indexes.
public List<CellIndex> GetNeighbors(int rowIndex, int colIndex)
{
var rowIndexes = (new int[] { rowIndex - 1, rowIndex, rowIndex + 1 }).Where(n => n >= 0 && n < Rows);
var colIndexes = (new int[] { colIndex - 1, colIndex, colIndex + 1 }).Where(n => n >= 0 && n < Cols);
return (from row in rowIndexes from col in colIndexes where row != rowIndex || col != colIndex select new CellIndex { Row = row, Col = col }).ToList();
}
private ArrayList<Element> getNeighbors(Element p) {
ArrayList<Element> n = new ArrayList<Element>();
for (int dr = -1; dr <= +1; dr++) {
for (int dc = -1; dc <= +1; dc++) {
int r = p.row + dr;
int c = p.col + dc;
if ((r >= 0) && (r < ROWS) && (c >= 0) && (c < COLS)) {
// skip p
if ((dr != 0) || (dc != 0))
n.add(new Element(r, c));
}
}
}
return n;
}
although nested for loops in list comprehensions is a bit ugly this is shorter:
def neighbours(m, i, j):
return [m[x][y] for x in [i-1,i,i+1] for y in [j-1,j,j+1] if x in range(0,len(m)) and y in range(0,len(m[x])) and (x,y) != (i,j)]
here is some code for C#:
public Cell[,] MeetNeigbours(Cell[,] Grid)
{
for (int X = 0; X < Grid.GetLength(0); X++)
{
for (int Y = 0; Y < Grid.GetLength(1); Y++)
{
int NeighbourCount = 0;
for (int i = -1; i < 2; i++)
{
for (int j = -1; j < 2; j++)
{
if (CellExists(Grid, (X + i)), (Y + j) && (i != 0 && j != 0))
{
Grid[X, Y].Neighbours[NeighbourCount] = Grid[(X + i), (Y + j)];
}
if(!(i == 0 && j == 0))
{
NeighbourCount++;
}
}
}
}
}
return Grid;
}
public bool CellExists(Cell[,] Grid, int X, int Y)
{
bool returnValue = false;
if (X >= 0 && Y >= 0)
{
if (X < Grid.GetLength(0) && Y < Grid.GetLength(1))
{
returnValue = true;
}
}
return returnValue;
}
with the "Cell" class looking like this:
public class Cell
{
public Cell()
{
Neighbours = new Cell[8];
}
/// <summary>
/// 0 3 5
/// 1 X 6
/// 2 4 7
/// </summary>
public Cell[] Neighbours;
}
This was really helpful to me in a recent project, so here's #Seb 's pseudo-code implementation in swift. This is assuming that the two-dimensional array is square:
func adjacentIndexPaths(to indexPath: IndexPath) -> [IndexPath] {
var neighboringSquareIndexes: [IndexPath] = []
// gridSquareCount is the size of the 2D array. For example, in an 8 x 8 [[Array]], gridSquareCount is 8
let maxIndex = gridSquareCount - 1
var neighborRowIndex = max(0, indexPath.section - 1)
var neighborColumnIndex = max(0, indexPath.row - 1)
while neighborRowIndex <= min(indexPath.section + 1, maxIndex) {
while neighborColumnIndex <= min(indexPath.row + 1, maxIndex) {
if neighborRowIndex != indexPath.section || neighborColumnIndex != indexPath.row {
neighboringSquareIndexes.append(IndexPath(row: neighborColumnIndex, section: neighborRowIndex))
}
neighborColumnIndex += 1
}
neighborRowIndex += 1
neighborColumnIndex = max(0, indexPath.row - 1)
}
return neighboringSquareIndexes }
In javascript
let arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
function getNeighborsNumbersAtIthJth(i, j) {
let allPosibleIndexes = [
[i - 1, j],
[i, j - 1],
[i - 1, j - 1],
[i + 1, j],
[i, j + 1],
[i + 1, j + 1],
[i + 1, j - 1],
[i - 1, j + 1]
];
let allPosibleValues = []
allPosibleIndexes.forEach(([i, j]) => {
try {
allPosibleValues.push(arr[i][j])
} catch (err) {
}
})
return allPosibleValues.filter(v => v != undefined);
}
console.log(getNeighborsNumbersAtIthJth(1, 1));//[2, 4, 1, 8, 6, 9, 7, 3]
console.log(getNeighborsNumbersAtIthJth(0, 1));//[1, 5, 3, 6, 4]
console.log(getNeighborsNumbersAtIthJth(0, 0));//[4, 2, 5]
I use a directions array and run a loop to get appropriate directions. Something like this (code is in JS)
function getAdjacent(matrix, i, j, k) {
const directions = [
[i - 1, j - 1],
[i - 1, j],
[i - 1, j + 1],
[i, j - 1],
[i, j + 1],
[i + 1, j - 1],
[i + 1, j],
[i + 1, j + 1],
];
const [row, col] = directions[k];
// Check for last rows and columns
if (row < 0 || row >= matrix.length || col < 0 || col >= matrix[i].length) {
return undefined;
}
return matrix[row][col];
}
function run(){
const hello = 'hello';
const matrix = [
[1, 2, 1],
[2, 1, 1],
[1, 1, 1]
];
for (let i = 0; i < matrix.length; i++) {
for (let j = 0; j < matrix[i].length; j++) {
let sum = 0;
for (let k = 0; k < 8; k++) {
const res = getAdjacent(matrix, i, j, k);
console.log(i, j, k, res); // Do whatever you want here
}
}
}
}
run();
This example in Python might also shed some light:
from itertools import product
def neighbors(coord: tuple, grid=(10, 10), diagonal=True):
"""Retrieve all the neighbors of a coordinate in a fixed 2d grid (boundary).
:param diagonal: True if you also want the diagonal neighbors, False if not
:param coord: Tuple with x, y coordinate
:param grid: the boundary of the grid in layman's terms
:return: the adjacent coordinates
"""
width = grid[0] - 1
height = grid[1] - 1
retx, rety = coord
adjacent = []
nb = [x for x in product([-1, 0, 1], repeat=2) if x != (0, 0)]
if not diagonal:
nb = [x for x in nb if x not in product([-1, 1], repeat=2)]
for x, y in nb:
xx = retx + x
yy = rety + y
if xx < 0 or xx > width or yy < 0 or yy > height:
# not within its boundaries
continue
adjacent.append((xx, yy))
return adjacent
the first product line (nb = [x for x in product([-1, 0, 1], repeat=2) if x != (0, 0)]) will produce all the coordinates of its neibors including the diagonal ones. The (0,0) is removed because that is ourselves so not a neighbor :-)
[(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]
If you do not want the diagonal neighbors you can tell it to remove those (product([-1, 1], repeat=2)) then the boundaries of the grid are checked and the resulting list of coordinates will be produced.
Ruby => Returns an array of neighbours.
array = [
[1, 2, 5, 6],
[8, 89, 44, 0],
[8, 7, 23, 0],
[6, 9, 3, 0]
]
def neighbours(array, (i , j))
[
[i, j - 1],
[i, j + 1],
[i - 1, j - 1],
[i - 1, j],
[i - 1, j + 1],
[i + 1, j - 1],
[i + 1, j],
[i + 1, j + 1],
].select { |h, w|
h.between?(0, array.length - 1) && w.between?(0, array.first.length - 1)
}.map do |row, col|
array[row][col]
end
end
array.each_with_index do |row, i|
row.each_with_index do |col, j|
p(array[i][j], neighbours(array, [i, j]))
end
end
I know this is an older question. However, I want to post a solution that I wrote based on Shubh Tripathi's answer
If we're looking for the same neighbors every time and want efficient bounds checking, there is a simple way to achieve this by just storing the indexes we want in an array without re-generating them in each iteration.
I wrote this for a simulation, where I wanted to check all directions surrounding an entity.
def get_surroundings(self, position):
"""
For a given grid location, it returns the surrounding 8x8 grid.
Indexed from the top left to the bottom right. (row-wise)
Args:
position (tuple): The position of the grid location.
Returns:
list: The surrounding 8x8 grid.
"""
# set the x and y coordinates
x = position[0]
y = position[1]
# list out the relative locations of the neighbors
surroundings = [
(-1, -1), (-1, 0), (-1, 1),
(0, -1), (0, 1),
(1, -1), (1, 0), (1, 1)
]
return_list = []
# go through the relative neighbours list, and check if any of the
# bounds condition fail. if they do, append none.
for neighbour in surroundings:
if (
x + neighbour[0] < 0 or
x + neighbour[0] >= self.grid_size or
y + neighbour[1] < 0 or
y + neighbour[1] >= self.grid_size
):
return_list.append(None)
else:
return_list.append(self.grid[x + neighbour[0]][y + neighbour[1]])
self.grid is your 2x2 grid.
Consider an array whose length will be always product of two numbers. For below array l is 4 and w is 5.
There is also a given index. I want to get the two arrays containing elements which lie on the diagonal line which passes through that particular index.
[
0, 1, 2, 3, 4
5, 6, 7, 8, 9
10, 11, 12, 13, 14
15, 16, 17, 18, 19
]
index = 7 => [3, 7, 11, 15] and [1, 7, 13, 19]
index = 16 => [4, 8, 12, 16] and [10, 16]
index = 0 => [0, 6, 12, 18] and [0]
I have tried following:
let arr = Array(20).fill().map((x,i) => i);
function getDias(arr, l, w, ind){
let arr1 = [];
let arr2 = [];
for(let i = 0;i<l;i++){
arr1.push(arr[ind + (i * w) + i])
arr1.push(arr[ind - (i * w) - i])
arr2.push(arr[ind + (i * w) + i])
arr2.push(arr[ind - (i * w) - i])
}
const remove = arr => [...new Set(arr.filter(x => x !== undefined))];
return [remove(arr1),remove(arr2)];
}
console.log(getDias(arr, 4, 5, 7))
The code have two problems. Both the arrays in the result are same. And secondly they are not in order.
Note: I don't want to use sort() to reorder the array. And also I don't want loop through all 20 elements. Just want to get the elements of that diagonal row
Some math (ie, modulus, integer division, and minimums) can find the beginning row and column for both the diagonal that runs left-to-right (LTR) and right-to-left (RTL), saving complexity of iterating backwards to find the starting points. Then, using those beginning rows and columns, simply iterate until outside the height and width bounds of the array.
let arr = Array(20).fill().map((x, i) => i);
function diagonals(arr, h, w, n) {
var nRow = Math.floor(n / w);
var nCol = n % w;
let LTR = [];
for (let r = nRow - Math.min(nRow, nCol), c = nCol - Math.min(nRow, nCol); r < h && c < w; r++, c++) LTR.push(arr[r * w + c]);
let RTL = [];
for (let r = nRow - Math.min(nRow, w - nCol - 1), c = nCol + Math.min(nRow, w - nCol - 1); r < h && 0 <= c; r++, c--) RTL.push(arr[r * w + c]);
return [LTR, RTL];
}
Sample runs...
diagonals(arr, 4, 5, 7); // returns ==> [[1, 7, 13, 19], [3, 7, 11, 15]]
diagonals(arr, 4, 5, 15); // returns ==> [[15], [3, 7, 11, 15]]
Edit: Note on arr value vs index.
Also, just a point of clarification. The question indicates "There is also a given index. I want to get the two arrays containing elements which lie on the diagonal line which passes through that particular index." If the index of the rectangular array is being sought rather than the actual value of arr, then there is no need to build arr, and subsequently the function and push statements can be changed to...
function diagonals(h, w, n)
LTR.push(r * w + c)
RTL.push(r * w + c)
Try the following code
let arr = Array(20).fill().map((x,i) => i);
function getDias(arr, l, w, ind){
let arr1 = [];
let arr2 = [];
n = l*w;
lVal = Math.floor(ind/w);
rVal = ind%w;
temp1 = lVal;
temp2 = rVal;
while(temp1>=0 && temp2>=0){
val = ((w*temp1) + temp2);
arr1.unshift(arr[val]);
temp1--;
temp2--;
}
temp1 = lVal;
temp2 = rVal;
temp1++;
temp2++;
while(temp1<l && temp2<w){
val = ((w*temp1) + temp2);
arr1.push(arr[val]);
temp1++;
temp2++;
}
console.log(arr1);
temp1 = lVal;
temp2 = rVal;
while(temp1>=0 && temp2<w){
val = ((w*temp1) + temp2);
arr2.unshift(arr[val]);
temp1--;
temp2++;
}
temp1 = lVal;
temp2 = rVal;
temp1++;
temp2--;
while(temp1<l && temp2>=0){
val = ((w*temp1) + temp2);
arr2.push(arr[val]);
temp1++;
temp2--;
}
console.log(arr2);
}
getDias(arr, 4, 5, 7);
getDias(arr, 4, 5, 16);
getDias(arr, 4, 5, 0);
The idea is to compute l_val and r_val.
l_val = index/w
r_val = index%w
Now arr[l_val][r_val] marks the position in the matrix found by l_val* w+ r_val
This is now followed by 4 steps:
1) Start iteration from arr[l_val][r_val]. Subtract 1 from both till we reach the end. Unshift this to array_1 (to maintain order)
2) Start iteration from [l_val][r_val].Add 1 to both till we reach the end. Push this to array_1.
3) Start iteration from arr[l_val][r_val]. Subtract 1 from l_val and add 1 t r_val till we reach the end. Unshift this to array_2 (to maintain order)
4) Start iteration from [l_val][r_val].Add 1 to l_val and subtract 1 from r_val till we reach the end. Push this to array_2.
let arr = Array(20).fill().map((x, i) => i);
function getDias(arr, l, w, ind) {
let arr1 = [];
let arr2 = [];
let row = parseInt(ind / w);
let col = ind - row * w;
diagIteration(arr, row, col, l, w, -1, arr1);
diagIteration(arr, row, col, l, w, 1, arr2);
return [arr1, arr2];
}
function diagIteration(arr, row, col, l, w, strategy, result) {
// find the starting row and col to begin with
while (row - 1 >= 0 && col + strategy >= 0 && col + strategy < w) {
row--;
col += strategy;
}
// iterate the diagonal elements and add it in result
strategy *= -1; // since we need to diagonally the reverse way after getting the base indexes to begin with
for (; row >= 0 && row < l && col >= 0 && col < w; row++, col += strategy) result.push(arr[row * w + col]);
}
// tests
for (var i = 0; i < arr.length; ++i) {
console.log(arr[i] + ' =>', getDias(arr, 4, 5, i));
}
We compute the row and column for a particular given index. For any given index, the row would be index / width and column would be index - row * width.
Now, we find the base indexes to begin with and iterate over the diagonal as if were a 2D array. The index lying on any diagonal can be computed as row * width + column. We do an additional check to see if we are still in range of the simulated 2D array.
/* you need to start your loop with respect of R and C; if you find the element then you need to break the loop and follow same process.*/
function Diagonals(R, C, matrix, K)
{
for(let i=0; i<R;i++)
{
for(let j=0; j<C; j++)
{
if(matrix[i][j]==K)
{
r=i;
c=j;
break;
}
}
}
let sum = r+c;
let diff= r-c;
let left= "";
let right="";
for(let i=0; i<R; i++){
for(let j=0; j<C; j++){
if(i-j==diff){
left=left+matrix[i][j]+" ";
}
if(i+j==sum)
{
right=right+matrix[i][j]+" ";
}
}
}
console.log(left);
console.log(right);
}
In Javascript, I have a scenario to implement the following:
Let's take a list of integer below, take each integer in that list and double the value, then square the doubled value, then as output, sum together all the squared values. E.g.:
doubleandSquareandSum[1] // 4
doubleandSquareandSum[1, 2] // 20
doubleandSquareandSum[1, 2, 3] // 56
doubleandSquareandSum[1, 2, 3, 4] // 120
JS Code tried:
function doubleandSquareandSum(arr) {
ret= [];
for (var i = 0, len = arr.length; i < len; i++) {
ret.push(arr[i] * arr[i]);
}
return ret;
}
I know Math.sqrt, but not sure how to implement it here.
I have tried something above, but didn't worked. Can someone help me out?
I would reduce, adding the square of each doubled item to the accumulator:
const doubleandSquareandSum = arr => arr.reduce((a, num) => a + ((2 * num) ** 2), 0);
console.log(doubleandSquareandSum([1])) // 4
console.log(doubleandSquareandSum([1, 2])) // 20
console.log(doubleandSquareandSum([1, 2, 3])) // 56
console.log(doubleandSquareandSum([1, 2, 3, 4])) // 120
Instead of pushing the value in array again, just calculate the new value and add it to sum variable.
doubleandSquareandSum([1]) // 4
doubleandSquareandSum([1, 2]) // 20
doubleandSquareandSum([1, 2, 3]) // 56
doubleandSquareandSum([1, 2, 3, 4]) // 120
function doubleandSquareandSum(arr) {
var sum = 0;
for (var i = 0, len = arr.length; i < len; i++) {
sum += 4 * arr[i] * arr[i];
}
console.log(sum);
return sum;
}
You could take a functional approach by taking single small functions to build intermediate results and the final result.
const
twice = v => 2 * v,
square = v => v * v,
pipe = fn => v => fn.reduce((x, f) => f(x), v),
twiceAndSquare = pipe([twice, square]),
add = (a, b) => a + b,
doubleandSquareandSum = array => array.map(twiceAndSquare).reduce(add);
console.log(doubleandSquareandSum([1])); // 4
console.log(doubleandSquareandSum([1, 2])); // 20
console.log(doubleandSquareandSum([1, 2, 3])); // 56
console.log(doubleandSquareandSum([1, 2, 3, 4])); // 120
I have three groups:
Group A = 4
Group B = 8
Group C = 11
For each group, if I add an additional item, for example:
Group A = 4;
Group A Additional = 2;
Group B = 8;
Group B Additional = 3;
Group C = 10;
Group C Additional = 3;
I need to add the additional depending on how many groups are available.
Example:
A = 4
AA = 6 (4 + 2)
AAA = 8 (4 + 2 + 2) here I have A and 2 additional items
B = 8
BB = 11 (8 + 3)
BBB = 14 (8 + 3 + 3)
Same for C...
I need to generate all possible combinations of these groups with the correct values.
This should be the result:
A, AA, AAA
B, BA, BAA, BB, BBA, BBB
C, CA, CAA, CB, CBA, CBB, CC, CCA, CCB, CCC
If AAA is 8 (4 + 2 + 2) and BBB = 14 (8 + 3 + 3)
I want:
CBA to be 22 (10 + 8 + 4)
CBB to be (10 + 8 + 8)
and so on.
This is what I made until now.
items = [
{ :group => "A", :value=> 4, :add => 2 },
{ :group => "B", :value=> 8, :add => 3 },
{ :group => "C", :value=> 10, :add => 3 },
]
def process(items)
array = []
items.each_with_index do |item, index|
counter = 0
(1..3).each do |vari|
el = item[:group] * vari
if vari == 1
value = item[:value]
else
value = item[:value] + (item[:add] * counter)
end
puts "#{el}: #{value}"
array.push(el)
counter = counter + 1
end
end
array
end
It only works for
A, AA, AAA
B, BB, BBB
C, CC, CCC
Output:
A: 4
AA: 6
AAA: 8
B: 8
BB: 11
BBB: 14
C: 10
CC: 13
CCC: 16
Can anyone help to complete the script?
You could use an recursive approach by taking the index for the next call of the function.
This solution takes a dummy item for getting single or double combination.
function c(array, size) {
function iter(i, p) {
var temp = p.join('');
if (i >= array.length) {
return;
}
if (p.length === size) {
temp && result.push(temp);
return;
}
iter(i + 1, p);
iter(i, p.concat(array[i]));
}
var result = [];
iter(0, []);
return result;
}
var values = { A: { value: 4, add: 2 }, B: { value: 8, add: 3 }, C: { value: 10, add: 3 } },
combinations = c(['C', 'B', 'A', ''], 3),
result = combinations.map(s => [...s].reduce(
(r, k, i, a) => r + values[k][['value', 'add'][+(k === a[i - 1])]],
0
));
console.log(combinations);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Just out of curiosity, ruby:
w = {:a=>[4, 2], :b=>[8, 3], :c=>[10, 3], nil =>[0, 0]}
[1, 2, 3].flat_map do |count|
%i[a b c].repeated_combination(count).map do |a, b, c|
[
[a, b, c].join.upcase,
a == b ?
w[a].first + (b == c ? w[a].last * 2 : w[a].last + w[c].first) :
[w[a], w[b], w[c]].map(&:first).reduce(:+)
]
end
end.to_h
#⇒ {"A"=>4, "B"=>8, "C"=>10,
# "AA"=>6, "AB"=>12, "AC"=>14, "BB"=>11, "BC"=>18, "CC"=>13,
# "AAA"=>8, "AAB"=>14, "AAC"=>16, "ABB"=>20, "ABC"=>22,
# "ACC"=>24, "BBB"=>14, "BBC"=>21, "BCC"=>28, "CCC"=>16}
I want to convert this nested loops in recursion. How do I achieve this?
for(let i = 0; i < 5; i++) {
for(let j = 0; j < 5; j++) {
console.log(i,j);
}
}
Here another example of this recursion:
function loop(i,j,limitI,limitJ){
if(i>=limitI) return;
if(j>=limitJ) loop(i+1,0,limitI,limitJ);
else{
console.log(i,j);
loop(i,j+1,limitI,limitJ)
}
}
loop(0,0,4,4);
Generic function product calculates the Cartesian product of its inputs - You can polyfill Array.prototype.flatMap if it's not already in your environment
Array.prototype.flatMap = function (f, context)
{
return this.reduce ((acc, x) => acc.concat (f (x)), [])
}
const product = (first = [], ...rest) =>
{
const loop = (comb, first, ...rest) =>
rest.length === 0
? first.map (x => [ ...comb, x ])
: first.flatMap (x => loop ([ ...comb, x ], ...rest))
return loop ([], first, ...rest)
}
const suits =
['♤', '♡', '♧', '♢']
const ranks =
['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
for (const card of product (ranks, suits))
console.log (card)
// [ 'A', '♤' ]
// [ 'A', '♡' ]
// [ 'A', '♧' ]
// [ 'A', '♢' ]
// [ '2', '♤' ]
// ...
// [ 'Q', '♧' ]
// [ 'K', '♤' ]
// [ 'K', '♡' ]
// [ 'K', '♧' ]
// [ 'K', '♢' ]
product is a variadic function (by use of a rest parameter) which accepts 1 or more inputs
const range = (min = 0, max = 0) =>
max < min
? []
: [ min, ...range (min + 1, max) ]
const r =
range (0, 2)
for (const comb of product (r, r, r))
console.log (comb)
// [ 0, 0, 0 ]
// [ 0, 0, 1 ]
// [ 0, 0, 2 ]
// [ 0, 1, 0 ]
// ...
// [ 2, 1, 2 ]
// [ 2, 2, 0 ]
// [ 2, 2, 1 ]
// [ 2, 2, 2 ]
Using destructuring assignment, you can effectively create nested loops
for (const [ i, j ] of product (range (0, 5), range (0, 5)))
console.log ("i %d, j %d", i, j)
// i 0, j 0
// i 0, j 1
// i 0, j 2
// i 0, j 3
// i 0, j 4
// i 0, j 5
// i 1, j 0
// ...
// i 4, j 5
// i 5, j 0
// i 5, j 1
// i 5, j 2
// i 5, j 3
// i 5, j 4
// i 5, j 5
product can also be written using generators - below, we find all perfect Pythagorean triples under 20
const product = function* (first, ...rest)
{
const loop = function* (comb, first, ...rest)
{
if (rest.length === 0)
for (const x of first)
yield [ ...comb, x ]
else
for (const x of first)
yield* loop ([ ...comb, x ], ...rest)
}
yield* loop ([], first, ...rest)
}
const range = (min = 0, max = 0) =>
max < min
? []
: [ min, ...range (min + 1, max) ]
const pythagTriple = (x, y, z) =>
(x * x) + (y * y) === (z * z)
const solver = function* (max = 20)
{
const N = range (1, max)
for (const [ x, y, z ] of product (N, N, N))
if (pythagTriple (x, y, z))
yield [ x, y, z ]
}
console.log ('solutions:', Array.from (solver (20)))
// solutions:
// [ [ 3, 4, 5 ]
// , [ 4, 3, 5 ]
// , [ 5, 12, 13 ]
// , [ 6, 8, 10 ]
// , [ 8, 6, 10 ]
// , [ 8, 15, 17 ]
// , [ 9, 12, 15 ]
// , [ 12, 5, 13 ]
// , [ 12, 9, 15 ]
// , [ 12, 16, 20 ]
// , [ 15, 8, 17 ]
// , [ 16, 12, 20 ]
// ]
I think using map (and reduce), while it allows for more complex recursive structures as you demonstrate, is actually an implicit for loop, which does not really answer the question on how to convert one into a recurrence. However, if you also defined a recursive map and reduce, then it would be OK :) - גלעד ברקן
Your wish is my command :D
const Empty =
Symbol ()
const concat = (xs, ys) =>
xs.concat (ys)
const append = (xs, x) =>
concat (xs, [ x ])
const reduce = (f, acc = null, [ x = Empty, ...xs ]) =>
x === Empty
? acc
: reduce (f, f (acc, x), xs)
const mapReduce = (m, r) =>
(acc, x) => r (acc, m (x))
const map = (f, xs = []) =>
reduce (mapReduce (f, append), [], xs)
const flatMap = (f, xs = []) =>
reduce (mapReduce (f, concat), [], xs)
const product = (first = [], ...rest) =>
{
const loop = (comb, first, ...rest) =>
rest.length === 0
? map (x => append (comb, x), first)
: flatMap (x => loop (append (comb, x), ...rest), first)
return loop ([], first, ...rest)
}
const suits =
['♤', '♡', '♧', '♢']
const ranks =
['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
for (const card of product (ranks, suits))
console.log (card)
// same output as above
I don't recommend this but you can do following(as it is difficult to read, for readability and understandability your code is best).
function forLoop(i,j){
if(j===0){
if(i!==0)
forLoop(i-1,4);
console.log(i,j);
}
else{
forLoop(i,j-1);
console.log(i,j);
}
}
forLoop(4,4);
Here's my rendition:
function nested(i, j, maxI, maxJ) {
if (i == maxI) return
console.log(i, j)
if (i < maxI) {
++j < maxJ ? nested(i, j, maxI, maxJ) : nested(++i, 0, maxI, maxJ)
}
}
nested(0, 0, 5, 5)
This is an alternative.
This approach uses param initialization with comma operator (just to make the code shorter).
Additionally, an operator param (callback) to execute any logic for each iteration.
function loop(n, operator, i = 0, j = 0) { // Param initialization.
if (j === n) (j = 0, i++); // Comma operator.
if (i === n) return;
operator(i, j);
loop(n, operator, i, ++j);
}
loop(5, (i, j) => console.log(i, j));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could use an array for limit and values. The order is reversed, because of the incrementing of the lowest index first.
This works for an arbitrary count of nested loops and it allowes to use an arbitrary limit of the max values.
function iter(limit, values = limit.map(_ => 0)) {
console.log(values.join(' '));
values = values.reduce((r, v, i) => {
r[i] = (r[i] || 0) + v;
if (r[i] >= limit[i]) {
r[i] = 0;
r[i + 1] = (r[i + 1] || 0) + 1;
}
return r;
}, [1]);
if (values.length > limit.length) {
return;
}
iter(limit, values);
}
iter([2, 3]);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here's an outline of a "recurrence relation," where "each further term of the sequence ... is defined as a function of the preceding terms."
As you are probably aware, recursive functions usually have at least one base case, terminating the recursion, and at least one recursive call. To find a pattern, let's examine the sequence:
0,0
0,1
0,2
0,3
0,4
1,0
1,2
...
Our base case, where the call to a preceding parameter terminates, seems to be 0,0. But this is also where the console logs begin, which means we first have to call all the way back to the base case. For convenience, let's assume the function expects positive parameters:
function f(i, j){
if (i == 0 && j == 0){
console.log(i,j);
return;
}
}
We can also notice that the outer loop, the i, stays constant for each cycle of js:
function f(i, j){
if (i == 0 && j == 0){
console.log(i,j);
return;
}
if (j == 0)
// ... what happens here?
}
but here we get stuck. When j is greater than zero, we can determine that the current term came from f(i, j - 1), but if j is zero in the current term, we have no way of formulating what it was in the preceding term. We need one more parameter:
function f(i, j, jj){
if (i == 0 && j == 0){
console.log(i,j);
return;
}
if (j == 0)
f(i - 1, jj, jj);
else
f(i, j - 1, jj);
console.log(i,j);
}
f(4,4,4);
You could recurse by taking a depth and the values to iterate:
function loop(start, end, depth, exit, ...args){
for(let i = start; i < end; i++)
depth ? loop(start, end, depth - 1, exit, ...args, i) : exit(...args, i);
}
Usable as:
loop(0, 5, 1, (i, j) => console.log(i, j))
The only real usecase would be deeper loops, e.g. this one
If you want it completely without for:
const range = (start, end, cb) =>
(cb(start), start + 1 >= end || range (start + 1, end, cb));
function loop(start, end, depth, exit, ...args){
range(start, end, i =>
depth ? loop(start, end, depth - 1, exit, ...args, i) : exit(...args, i));
}
Try it
Transforming a nested for loop into its recursive counterpart is surprisingly hard. Good question!
You can transform every loop (without a stack) into a tail recursive algorithm. So this rule should hold for a nested loop too.
I think we need two distinct functions to get something equivalent to your two nested loops:
const loop = ([i, j], [k, l]) => {
const loop_ = (k_, l_) => {
if (k_ >= l_) return;
else {
console.log(i, k_);
loop_(k_ + 1, l_);
}
};
if (i >= j) return;
else {
loop_(k, l);
loop([i + 1, j], [k, l]);
}
};
loop([0, 5], [0, 5]);
You have to pass ranges for both the out and the inner loop.
As you can see both recursive calls are in tail position. I think this is the closest equivalent we can get.
suggested solution
function recurse(arg1=0, arg2=0, cb) {
if ( arg2 <= 5 ) {
let _l = arg2++;
if ( arg1 === 5 )
return ;
if ( ++_l === 6 ) {
arg2 = 0;
cb(arg1++, arg2);
recurse(arg1, arg2, cb);
} else {
cb(arg1, arg2 - 1);
recurse(arg1, arg2, cb);
}
}
}
recurse( 0 , 0 , (i,j) => console.log(i,j));