4

I have to sort through a (rows)array of (row)arrays. The (row)arrays contain an arbitrary number of strings. If a (row)array contains only empty strings I want to remove it from the (rows)array.

I'm currently doing this:

rows.each do |row|

  row.each_index do |i|

   if row[i].length > 0
    break 
   elsif i == row.count-1
    rows.delete(row)
   end

  end

end

But is there a more elegant way to do it?

Undistraction
  • 42,754
  • 56
  • 195
  • 331

6 Answers6

5

Slightly more concise:

rows.reject! { |row| row.all?(&:empty?) }
Alex D
  • 29,755
  • 7
  • 80
  • 126
  • 1
    When calling a method, `&expression` means "evaluate `expression`, call `to_proc` on whatever it returns, and use the value returned by `to_proc` as the block for this method call". `Symbol#to_proc` works like this: `:method_name.to_proc` returns an anonymous function which is just like `{ |x| x.method_name }`. – Alex D Oct 08 '12 at 20:43
  • So when you are passing a block which looks like `{ |x| x.method }`, you can always compress that down to `&:method`, which should go last in the list of arguments. – Alex D Oct 08 '12 at 20:44
  • Nice further discussion of & / to_proc here: http://stackoverflow.com/questions/1961030/ruby-ruby-on-rails-ampersand-colon-shortcut – Undistraction Oct 08 '12 at 20:48
4

Modifying an array while you iterate though it is not a good idea - you may find your code skips certain elements or does weird stuff. I'd do

rows.reject! {|row| row.all? {|row_element| row_element.empty?}}

We reject a row if the block row_element.empty? evaluates to true for all elements in the row. It's well worth getting familiar with all of the methods in Enumerable, they're very handy for this sort of task.

Frederick Cheung
  • 83,189
  • 8
  • 152
  • 174
  • It's better to use blank? instead of empty? in case one of elements is nil, blank? will return true, while empty? will error out – iouri Oct 08 '12 at 19:10
  • Thanks a lot. For clarity please edit your answer to show that this will return a new array, leaving rows untouched because you used reject instead of reject! Did you do this because using reject! would cause rows to be modified during iteration? – Undistraction Oct 08 '12 at 19:13
  • 2
    +1 Since the OP asked for an elegant solution, not to say yours is not, it is perfectly fine, consider this: `rows.reject{ |row| row.all? &:empty? }` – Kyle Oct 08 '12 at 19:22
  • @iouri blank? is a railsism, although I suppose the question is tagged rails too. 1ndivisible - didn't realise you were interested in doing it in place - that works too – Frederick Cheung Oct 08 '12 at 19:30
  • Actually I think rows.reject! would be bad in this situation as 'The array is changed instantly every time the block is called and not after the iteration is over' (http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-reject) so this is equivalent to modifying the array during iteration as I was doing. Maybe rows = rows.reject! { ... } would be better? – Undistraction Oct 08 '12 at 19:36
  • Hehe railsism :) I made sure it was tagged with rails – iouri Oct 08 '12 at 19:40
  • reject! is fine - it's the entire point of it. `each` isn't expecting for the array to be modified which is why it's bad whereas reject! is the one doing the deleting so is able to keep everything in sync. – Frederick Cheung Oct 08 '12 at 20:27
4

when using rails:

! rows.any?(&:present?)
Harm de Wit
  • 2,150
  • 2
  • 18
  • 24
2

You can use compact.uniq or compact. If your arrays have nil values, compact will result in an empty array, so you can check for that like this:

row.compact.size == 0

if row contains empty strings "" you can check for it like this:

row.compact.uniq.first.blank? and row.size == 1
iouri
  • 2,919
  • 1
  • 14
  • 11
0
    rows.select{|row| row.compact.count >0}
mkz
  • 212
  • 2
  • 6
-1

Here: rows.all?(&:blank?).

Hugo Hernani
  • 165
  • 1
  • 10