337

I have the following array

cities = ["Kathmandu", "Pokhara", "", "Dharan", "Butwal"]

I want to remove blank elements from the array and want the following result:

cities = ["Kathmandu", "Pokhara", "Dharan", "Butwal"]

Is there any method like compact that will do it without loops?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
ashisrai_
  • 6,438
  • 2
  • 26
  • 42
  • 4
    Might be worth updating the accepted answer to @Marian13's [answer](https://stackoverflow.com/a/62719314/4055042) now Rails 6 has `compact_blank`. – SRack May 27 '22 at 10:00

21 Answers21

557

There are many ways to do this, one is reject

noEmptyCities = cities.reject { |c| c.empty? }

You can also use reject!, which will modify cities in place. It will either return cities as its return value if it rejected something, or nil if no rejections are made. That can be a gotcha if you're not careful (thanks to ninja08 for pointing this out in the comments).

Matt Greer
  • 60,826
  • 17
  • 123
  • 123
  • 248
    Or if you prefer more compact `cities.reject!(&:empty?)` – aNoble May 04 '11 at 04:55
  • 58
    hm, why not `cities.reject!(&:blank?)`? `empty?` is for arrays – Nico Jul 17 '12 at 20:35
  • 30
    @Nico `blank?` is only available through `ActiveSupport`. Standard Ruby does use `String#empty?`: http://www.ruby-doc.org/core-1.9.3/String.html#method-i-empty-3F – Michael Kohl Sep 10 '12 at 15:00
  • 29
    `reject` is better than `reject!` because `[].reject!(&:empty?)` returns `nil`, `[].reject(&:empty?)` returns `[]` – konyak Apr 29 '15 at 18:06
  • @aNoble what do you call that feature that you mentioned? – Nick Res Aug 09 '15 at 01:27
  • 19
    watch out with reject!. reject! will return nil if no changes are made to the array. If you want to return the array when no changes have been made, just use reject without the bang. – Nick Res Aug 09 '15 at 01:31
  • 1
    @ninja08 Honestly, I'm not sure if there is a common name for it. I suppose it's one form of the unary ampersand, so you could call it that. But the unary ampersand is also used for passing blocks around, not just for turning symbols into procs. – aNoble Aug 09 '15 at 08:22
  • @NickRes But doesn't `reject` without the bang create a shallow local copy? If you need the returned value, you could also do something like `cities.reject!(&:empty?); cities` to use the `!`/bang/in-situ variant. – Cadoiz Jul 18 '23 at 09:38
  • And @aNoble (ninja08's comment seems to be deleted): The common name for it seems to be [pretzel colon](https://www.designcise.com/web/tutorial/what-does-the-array-map-ampersand-colon-name-syntax-mean-in-ruby) – Cadoiz Jul 18 '23 at 09:45
185
1.9.3p194 :001 > ["", "A", "B", "C", ""].reject(&:empty?)

=> ["A", "B", "C"]
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
user2010324
  • 1,859
  • 1
  • 11
  • 2
  • 14
    I would prefer to use .reject(&:blank?) to avoid nil values as well – ran632 May 03 '16 at 12:07
  • 8
    @RanGalili `blank?` is a good choice but its a `rails` method, and this question is regarding the plain `ruby` – Swaps Oct 22 '16 at 05:49
114

Here is what works for me:

[1, "", 2, "hello", nil].reject(&:blank?)

output:

[1, 2, "hello"]
kimerseen
  • 2,511
  • 2
  • 16
  • 5
  • 1
    @Tom blank? is a Rails specific method. Doesn't exist on Array for plain ruby. You will have to use empty? or write your own method. – jpgeek Feb 07 '19 at 06:09
  • Because `NoMethodError: undefined method empty? for nil:NilClass` , `:blank? ` is better than `:empty?` – Lane Dec 09 '19 at 01:34
57

In my project I use delete:

cities.delete("")
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
esio
  • 1,592
  • 3
  • 17
  • 30
  • 6
    elegant! unfortunately doesn't return the remaining array, but pretty slick – Kevin Nov 03 '12 at 20:37
  • 12
    The Array.delete is counter-intuitive. It operates like a .delete!() if such a method existed. The .delete() operates directly on the array in a destructive manner. – scarver2 Jul 10 '13 at 20:36
55

compact_blank (Rails 6.1+)

If you are using Rails (or a standalone ActiveSupport), starting from version 6.1, there is a compact_blank method that removes blank values from arrays.

It uses Object#blank? under the hood for determining if an item is blank.

["Kathmandu", "Pokhara", "", "Dharan", nil, "Butwal"].compact_blank
# => ["Kathmandu", "Pokhara", "Dharan", "Butwal"]

[1, "", nil, 2, " ", [], {}, false, true].compact_blank
# => [1, 2, true]

Here is a link to the docs and a link to the relative PR.

A destructive variant is also available. See Array#compact_blank!.


If you have an older version of Rails, check compact_blank internal implementation.

It is not so complex to backport it.

def compact_blank
  reject(&:blank?)
end

If you need to remove only nil values, consider using Ruby build-in Array#compact and Array#compact! methods.

["a", nil, "b", nil, "c", nil].compact
# => ["a", "b", "c"]
Marian13
  • 7,740
  • 2
  • 47
  • 51
46

When I want to tidy up an array like this I use:

["Kathmandu", "Pokhara", "", "Dharan", "Butwal"] - ["", nil]

This will remove all blank or nil elements.

superluminary
  • 47,086
  • 25
  • 151
  • 148
  • 1
    Actually, The Tin Man's answer is better as it will also remove anything which matches Object#blank? i.e. nil, "", "\n", " ", "\n\r", etc. Unlike the accepted answer, it will also work without Rails. – superluminary Sep 23 '13 at 08:34
29

Most Explicit

cities.delete_if(&:blank?)

This will remove both nil values and empty string ("") values.

For example:

cities = ["Kathmandu", "Pokhara", "", "Dharan", "Butwal", nil]

cities.delete_if(&:blank?)
# => ["Kathmandu", "Pokhara", "Dharan", "Butwal"]
Joshua Pinter
  • 45,245
  • 23
  • 243
  • 245
phlegx
  • 2,618
  • 3
  • 35
  • 39
23

Try this:

puts ["Kathmandu", "Pokhara", "", "Dharan", "Butwal"] - [""]
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Raels
  • 400
  • 2
  • 8
  • 4
    This is slick, and doesn't return ""! This is a great lil trick. – Sean Larkin Jun 17 '13 at 18:45
  • 2
    ["Kathmandu", "Pokhara", " ", "Dharan", "Butwal"] - [""] - will not work in this case – ajahongir Oct 29 '14 at 07:53
  • 2.0.0-p247 :001 > ["Kathmandu", "Pokhara", "", "Dharan", "Butwal"] - [""] => ["Kathmandu", "Pokhara", "Dharan", "Butwal"] Seems to work for me. Why do you think it doesn't work? – Raels Oct 30 '14 at 01:07
  • 1
    @Raels, the blank string in this case is not empty. It has a single space within it. – Chandranshu Nov 13 '14 at 06:40
  • 1
    @Chandranshu I beg to differ. I copied and pasted the text into an editor and found there was no space between the quotes as you suggested. If there was, then subtracting ["", " "] would work. superluminary's example is similar and works as well. The original request was to remove "blank elements" not "elements that are blanks", and the example blank element was shown in the OP as "". – Raels Nov 15 '14 at 01:08
20

Use reject:

>> cities = ["Kathmandu", "Pokhara", "", "Dharan", "Butwal"].reject{ |e| e.empty? }
=> ["Kathmandu", "Pokhara", "Dharan", "Butwal"]
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
  • 14
    Or, as aNoble noted above, `reject(&:empty?)`. – mu is too short May 04 '11 at 05:00
  • Symbol to proc is slower in 1.8.7 than the block format. It is on par from what i've seen in 1.9.2 – Caley Woods May 04 '11 at 15:35
  • This is good as it will remove any element which matches Object#empty?, eg. "\n", " ", nil, "\n\n\n", etc... – superluminary Oct 07 '13 at 14:46
  • What version of Ruby/IRB? It works fine in 1.8.7-p358, 1.9.3-p448 and 2.0.0-p247. – the Tin Man Nov 25 '13 at 16:05
  • 5
    `nil.empty?` booom break! – Naveed Jan 15 '14 at 17:35
  • @Naveed, do not change people's code in their answers. You are welcome to improve formatting, spelling and grammar, but their code is theirs. Feel free to suggest improvements in a comment. The problem domain was for blank entries, not nil. – the Tin Man Jan 15 '14 at 17:50
  • 1
    @AllenMaxwell @Naveed If your array has nil elements, precede the `reject(&:empty?)` with `compact` e.g. `[nil, ''].compact.reject(&:empty?)` – scarver2 Jan 21 '15 at 19:45
  • Better yet, see [Map and Remove nil values in Ruby](http://stackoverflow.com/questions/13485468/map-and-remove-nil-values-in-ruby/13485482#13485482). Don't immediately reach for `compact`. Instead search to figure out why they are there and fix that problem because using `compact` often results in wasting CPU cycles doing something that could be avoided. – the Tin Man Jan 21 '15 at 21:44
  • `reject(&:empty?)` will work for empty strings, however it's safer to defend against `nil` at the same time with `reject(&:blank?)`, this will catch any `nil` or `""` entries in the array. – olvado Nov 29 '20 at 15:26
14
cities.reject! { |c| c.blank? }

The reason you want to use blank? over empty? is that blank recognizes nil, empty strings, and white space. For example:

cities = ["Kathmandu", "Pokhara", " ", nil, "", "Dharan", "Butwal"].reject { |c| c.blank? }

would still return:

["Kathmandu", "Pokhara", "Dharan", "Butwal"]

And calling empty? on " " will return false, which you probably want to be true.

Note: blank? is only accessible through Rails, Ruby only supports empty?.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Colto
  • 612
  • 4
  • 14
12

There are already a lot of answers but here is another approach if you're in the Rails world:

 cities = ["Kathmandu", "Pokhara", "", "Dharan", "Butwal"].select &:present?
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Naveed
  • 11,057
  • 2
  • 44
  • 63
  • 4
    `present?` comes from `ActiveSupport`. This has a no Rails tag and requiring an extra gem for one method seems excessive. – Michael Kohl Sep 10 '12 at 14:59
  • 1
    @Naveed, you should preface this with "If you're using RoR". I won't downvote it because it's still useful information for beginners. – pixelearth Jul 04 '13 at 00:28
10

Here is one more approach to achieve this

we can use presence with select

cities = ["Kathmandu", "Pokhara", "", "Dharan", nil, "Butwal"]

cities.select(&:presence)

["Kathmandu", "Pokhara", "Dharan", "Butwal"]
Sampat Badhe
  • 8,710
  • 7
  • 33
  • 49
  • 2
    Thank you for this. I had some `" "` elements in my array that were not removed with the reject method. Your method removed `nil` `""` or `" "` items. – iamse7en Oct 16 '15 at 00:39
10

To remove nil values do:

 ['a', nil, 'b'].compact  ## o/p =>  ["a", "b"]

To remove empty strings:

   ['a', 'b', ''].select{ |a| !a.empty? } ## o/p => ["a", "b"]

To remove both nil and empty strings:

['a', nil, 'b', ''].select{ |a| a.present? }  ## o/p => ["a", "b"]
vidur punj
  • 5,019
  • 4
  • 46
  • 65
8

Here is a solution if you have mixed types in your array:

[nil,"some string here","",4,3,2]

Solution:

[nil,"some string here","",4,3,2].compact.reject{|r| r.empty? if r.class == String}

Output:

=> ["some string here", 4, 3, 2]
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Francois
  • 10,465
  • 4
  • 53
  • 64
5

You can Try this

 cities.reject!(&:empty?)
anusha
  • 2,087
  • 19
  • 31
2

Shortest way cities.select(&:present?)

2
 cities = ["Kathmandu", "Pokhara", "", "Dharan", "Butwal"].delete_if {|c| c.empty? } 
suren
  • 969
  • 4
  • 22
1

Update in reject and reject!

NOTE: I came across this question and checked these methods on the irb console with ruby-3.0.1. I also checked the ruby docs but this is not mentioned there. I am not sure from which ruby version this change is there. Any help from the community is much appreciated.

With ruby-3.0.1 we can use either reject or reject!

cities = ["Kathmandu", "Pokhara", "", "Dharan", "Butwal"]
cities.reject{ |e| e.empty? }
=> ["Kathmandu", "Pokhara", "Dharan", "Butwal"]

or shorthand

cities.reject(&:empty?)
=> ["Kathmandu", "Pokhara", "Dharan", "Butwal"]

both will return [] no matter we have an empty value or not?

enter image description here

0

another method:

> ["a","b","c","","","f","g"].keep_if{|some| some.present?}
=> ["a","b","c","f","g"]
0

Plain Ruby:

values = [1,2,3, " ", "", "", nil] - ["", " ", nil]
puts values # [1,2,3]
Abel
  • 3,989
  • 32
  • 31
-3

Update with a strict with join & split

cities = ["Kathmandu", "Pokhara", "", "Dharan", "Butwal"]
cities.join(' ').split

Result will be:

["Kathmandu", "Pokhara", "Dharan", "Butwal"]

Note that: this doesn't work with a city with spaces

Hieu Pham
  • 6,577
  • 2
  • 30
  • 50