Project

General

Profile

Feature #17277

Make Enumerator#with_index yield row and col indices for Matrix

Added by greggzst (Grzegorz Jakubiak) about 1 month ago. Updated about 1 month ago.

Status:
Assigned
Priority:
Normal
Target version:
-
[ruby-core:100471]

Description

Given a matrix:

matrix = Matrix[[0,2,3,4], [6,7,8,9], [1,4,5,8]]

You could get the row and col indices of a matrix using Matrix#each_with_index:

matrix
.each_with_index { |e, row, col| p [row, col] }
[0, 0]
[0, 1]
[0, 2]
[0, 3]
[1, 0]
[1, 1]
[1, 2]
[1, 3]
[2, 0]
[2, 1]
[2, 2]
[2, 3] 

You can chain it with other enumerators and access indices within them:

matrix
.each_with_index
.filter_map { |e, row, col| [row, col] if e % 4 == 0}
# => [[0, 0], [0, 3], [1, 2], [2, 1], [2, 3]]

Meanwhile, with_index after Matrix#each returns flattened indices, not row or column indices, which does not look right:

matrix
.each.with_index { |e, index| p index }
0
1
2
3
4
5
6
7
8
9
10
11

I feel we should override with_index for Matrix so it returns row and column indices.

Updated by marcandre (Marc-Andre Lafortune) about 1 month ago

What about chained enumerators?

matrix.each(:diagonal).each_const(2).with_index do |elem, ?|

It's not clear to me how that could be implemented efficiently, do you have an idea?

Finally, what is the use case?

I would rather add map_with_index....

Updated by greggzst (Grzegorz Jakubiak) about 1 month ago

I also noticed when combining each_with_index with inject it passes element, row_index and col_index as one argument to the block

Matrix[[1,2,4,5],[7,8,9,2]].each_with_index.inject({}) { |acc, e, row, col| puts "#{acc}, #{e}, #{row}, #{col}"; acc[[e[1],e[2]]] = e[0]; acc } 
{}, [1, 0, 0], , 
{[0, 0]=>1}, [2, 0, 1], , 
{[0, 0]=>1, [0, 1]=>2}, [4, 0, 2], , 
{[0, 0]=>1, [0, 1]=>2, [0, 2]=>4}, [5, 0, 3], , 
{[0, 0]=>1, [0, 1]=>2, [0, 2]=>4, [0, 3]=>5}, [7, 1, 0], , 
{[0, 0]=>1, [0, 1]=>2, [0, 2]=>4, [0, 3]=>5, [1, 0]=>7}, [8, 1, 1], , 
{[0, 0]=>1, [0, 1]=>2, [0, 2]=>4, [0, 3]=>5, [1, 0]=>7, [1, 1]=>8}, [9, 1, 2], , 
{[0, 0]=>1, [0, 1]=>2, [0, 2]=>4, [0, 3]=>5, [1, 0]=>7, [1, 1]=>8, [1, 2]=>9}, [2, 1, 3], , 
=> {[0, 0]=>1, [0, 1]=>2, [0, 2]=>4, [0, 3]=>5, [1, 0]=>7, [1, 1]=>8, [1, 2]=>9, [1, 3]=>2}

marcandre (Marc-Andre Lafortune) wrote in #note-1:

What about chained enumerators?

matrix.each(:diagonal).each_const(2).with_index do |elem, ?|

It's not clear to me how that could be implemented efficiently, do you have an idea?

I tried with refinement for Enumerator in Matrix and use each_with_index to yield row and col index but couldn't get it working...

Finally, what is the use case?

I would like to access matrix indices in enumerable methods to perform some operations based on them.

I would rather add map_with_index....

I guess this would be helpful but I think it doesn't give enough flexibility because if you want to use with_index in select you don't need the map part and so on

Updated by nobu (Nobuyoshi Nakada) about 1 month ago

  • Assignee set to marcandre (Marc-Andre Lafortune)
  • Status changed from Open to Assigned

Updated by greggzst (Grzegorz Jakubiak) about 1 month ago

greggzst (Grzegorz Jakubiak) wrote in #note-2:

I also noticed when combining each_with_index with inject it passes element, row_index and col_index as one argument to the block

Matrix[[1,2,4,5],[7,8,9,2]].each_with_index.inject({}) { |acc, e, row, col| puts "#{acc}, #{e}, #{row}, #{col}"; acc[[e[1],e[2]]] = e[0]; acc } 
{}, [1, 0, 0], , 
{[0, 0]=>1}, [2, 0, 1], , 
{[0, 0]=>1, [0, 1]=>2}, [4, 0, 2], , 
{[0, 0]=>1, [0, 1]=>2, [0, 2]=>4}, [5, 0, 3], , 
{[0, 0]=>1, [0, 1]=>2, [0, 2]=>4, [0, 3]=>5}, [7, 1, 0], , 
{[0, 0]=>1, [0, 1]=>2, [0, 2]=>4, [0, 3]=>5, [1, 0]=>7}, [8, 1, 1], , 
{[0, 0]=>1, [0, 1]=>2, [0, 2]=>4, [0, 3]=>5, [1, 0]=>7, [1, 1]=>8}, [9, 1, 2], , 
{[0, 0]=>1, [0, 1]=>2, [0, 2]=>4, [0, 3]=>5, [1, 0]=>7, [1, 1]=>8, [1, 2]=>9}, [2, 1, 3], , 
=> {[0, 0]=>1, [0, 1]=>2, [0, 2]=>4, [0, 3]=>5, [1, 0]=>7, [1, 1]=>8, [1, 2]=>9, [1, 3]=>2}

My bad I just figured out that's a case for inject so it shouldn't be considered in this discussion.

Anyways it'd be good to have with_index chained to whatever enumerator returning Matrix indices

#5

Updated by sawa (Tsuyoshi Sawada) about 1 month ago

  • Description updated (diff)
#6

Updated by sawa (Tsuyoshi Sawada) about 1 month ago

  • Description updated (diff)

Updated by sawa (Tsuyoshi Sawada) about 1 month ago

I think the current behaviour is natural. You cannot play around with with_index since its receiver is Enumerator, not Matrix, and the information as a matrix is already gone.

You can retrieve the row and column indices easily from the flattened indices:

matrix.each.with_index{|e, index| p index.divmod(matrix.column_size)}
[0, 0]
[0, 1]
[0, 2]
[0, 3]
[1, 0]
[1, 1]
[1, 2]
[1, 3]
[2, 0]
[2, 1]
[2, 2]
[2, 3]

Updated by greggzst (Grzegorz Jakubiak) about 1 month ago

sawa (Tsuyoshi Sawada) wrote in #note-7:

I think the current behaviour is natural. You cannot play around with with_index since its receiver is Enumerator, not Matrix, and the information as a matrix is already gone.

You can retrieve the row and column indices easily from the flattened indices:

matrix.each.with_index{|e, index| p index.divmod(matrix.column_size)}
[0, 0]
[0, 1]
[0, 2]
[0, 3]
[1, 0]
[1, 1]
[1, 2]
[1, 3]
[2, 0]
[2, 1]
[2, 2]
[2, 3]

I get that but it means performing divmod calculation every time one needs to work row and col indices out. Couldn’t we maybe return a subclass of Enumerator that implements proper with_index for Matrix?

Also available in: Atom PDF