1

I'd like to filter a 2 dimensional array taking the desired column indices and returning a 2d array with only those columns. Except on an empty array I'd like to return the same 2 dimensional array.

Also I don't want to modify the original array, and I'd like to write my code without any if statements, or limit branching as much as possible.

I'm wondering if it is redundant to Marshal#load and Marshal#dump and use #map!.

There reason I chose to use #map! is so I don't have to use an if..else..end block, but I'm curious to learn other strategies.

Below is my solution:

def keep_columns args
  matrix = Marshal.load Marshal.dump args[:matrix]
  columns = args[:columns]
  matrix.map! do |row|
    row.select.with_index { |_,idx| columns.include? idx }
  end unless columns.empty?
  matrix
end

matrix = [['foo','bar', 'baz'],['cats', 'and', 'dogs']]

keep_columns matrix: matrix, columns: [0,2]
#=> [["foo", "baz"], ["cats", "dogs"]]
keep_columns matrix: matrix, columns: []
#=> [["foo", "bar", "baz"], ["cats", "and", "dogs"]]
mbigras
  • 7,664
  • 11
  • 50
  • 111

2 Answers2

2

You could use Array#transpose before and after Hash#values_at.

def extract_columns(arr, columns)
  return arr if columns.empty?
  arr.transpose.values_at(*columns).transpose
end

arr = [['foo','bar', 'baz'],['cats', 'and', 'dogs']]

extract_columns(arr, [0, 2])
  #=> [["foo", "baz"], ["cats", "dogs"]] 
extract_columns(arr, [])
  #=> [["foo", "bar", "baz"], ["cats", "and", "dogs"]]

Note that arr is not modified.

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
1

Try this

matrix.map(&:dup)

This creates a copy of the matrix.


Pro tipp — Your code does a linear search over columns for each element in the innermost loop, maybe use row.values_at(*columns) instead?

def keep_columns(args)
  columns, matrix = args.values_at(:columns, :matrix)
  return matrix.map(&:dup) if columns.empty?
  matrix.map { |row| row.values_at(*columns) }
end
akuhn
  • 27,477
  • 2
  • 76
  • 91
  • +1 for the pro tip, will upvote when I have my votes. Can you provide an example? Also, do you think your technique `matrix.dup.map(&:dup)` is applicable to [this question](http://stackoverflow.com/questions/41657091/use-replace-to-make-a-copy-of-an-array)? – mbigras Jan 20 '17 at 22:38
  • That question has a 1D array only. For 2D arrays `matrix.map(&:dup)` is correct, your marshalling code would also clone the elements of the matrix which you sure don't want to happen. – akuhn Jan 20 '17 at 22:45
  • 1
    Thank you @akuhn! Can you please change `(columns)` to `(*columns)`? :) – mbigras Jan 21 '17 at 02:05