How to compact empty elements in an array - ruby

Ruby compacts the sequence only if it has nil value, how do I compact "" empty value

Something like this:
a = [1, nil, 2, '', 3]
a.reject{|x| x == '' || x.nil?} # => [1, 2, 3]

A similar way of doing it to Sergio's:
irb(main):006:0> a = [1,nil,2,''] => [1, nil, 2, ""]
irb(main):007:0> a.reject!{|x| x.nil? || x == ''} => [1, 2]

I know this has no Ruby on Rails tag, but should you be using the framework, the best solution IMHO is:
a = [1, nil, 2, '', 3]
a.select(&:present?)
#=> [1, 2, 3]
In plain Ruby I'd go with Sergio's answer or a.select { |x| x.empty? || x.nil? } if the array can only contain Strings and nils.

Related

Remove nil and blank string in an array in Ruby

I am new to Ruby and stuck with this issue. Let's say I have an array like this:
arr = [1, 2, 's', nil, '', 'd']
and I want to remove nil and blank string from it, i.e. final array should be:
arr = [1, 2, 's', 'd']
I tried compact but it gives this:
arr.compact!
arr #=> [1, 2, 's', '', 'd'] doesn't remove empty string.
I was wondering if there's a smart way of doing this in Ruby.
You could do this:
arr.reject { |e| e.to_s.empty? } #=> [1, 2, "s", "d"]
Note nil.to_s => ''.
Since you want to remove both nil and empty strings, it's not a duplicate of How do I remove blank elements from an array?
You want to use .reject:
arr = [1, 2, 's', nil, '', 'd']
arr.reject { |item| item.nil? || item == '' }
NOTE: reject with and without bang behaves the same way as compact with and without bang: reject! and compact! modify the array itself while reject and compact return a copy of the array and leave the original intact.
If you're using Rails, you can also use blank?. It was specifically designed to work on nil, so the method call becomes:
arr.reject { |item| item.blank? }
I tend to do:
arr = [1, 2, 's', nil, '', 'd']
arr.reject(&:blank?)
returns:
=> [1, 2, "s", "d"]
arr.reject(&:blank?)
Just use this, no need to anything else.
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 which removes blank values from arrays.
It uses Object#blank? under the hood for determining if an item is blank.
[1, 2, 's', nil, '', 'd'].compact_blank
# => [1, 2, 's', 'd']
[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!.
You can also use - to remove all nil and '' elements:
arr -= [nil, '']
#=> [1, 2, "s", "d"]
Demonstration
Or compact and reject with shortcut (in case you are not using Rails where you can just use arr.reject(&:blank?) ):
arr = arr.compact.reject(&''.method(:==))
#=> [1, 2, "s", "d"]
Demonstration
You can use compact with reject
arr = [1, 2, 's', nil, '', 'd']
arr = [1, 2, 's', 'd']
arr = arr.compact.reject { |h| h == "" }
or
arr = arr.compact.delete_if { |h| h == "" }
The simplest and fast way of doing this is :
arr = [1, 2, 's', nil, '', 'd'] - [nil,'']
==> arr = [1, 2, 's', 'd']
You can use compact and delete_if method to remove nil and blank string in an array in Ruby
arr = [1, 2, 's', nil, '', 'd']
arr.compact!.delete_if{|arrVal| arrVal.class == String and arrVal.empty?}
=> [1, 2, "s", "d"]
try this out:
[1, 2, "s", nil, "", "d"].compact.select{|i| !i.to_s.empty?}
Note: I am considering the array might have string with white spaces in it.
You can do:
arr = [1, 2, 's', nil, ' ', 'd']
arr.reject{|a| a.nil? || (a.to_s.gsub(' ', '') == '') }
#=> [1, 2, "s", "d"]
or:
arr.reject{|a| a.nil? || (a.to_s.gsub(' ', '').empty?) }
#=> [1, 2, "s", "d"]
or if you want to update arr object itself then:
arr.reject!{|a| a.nil? || (a.to_s.gsub(' ', '') == '') } # notice the ! mark, it'll update the object itself.
p arr #=> [1, 2, "s", "d"]
Hope this will work for your case :
arr = [1, 2, 's', nil, '', 'd']
arr.select{|x| x.to_s!="" }
I would probably add .strip to eliminate potential whitespace headaches (assuming its not a rails app).
array = [1, 2, "s", nil, " ", "d", "\n"]
array.reject!{|a| a.nil? || (a.to_s.strip.empty?) }
#=> [1, 2, "s", "d"]

Reposition an element to the front of an array in Ruby

Even coming from javascript this looks atrocious to me:
irb
>> a = ['a', 'b', 'c']
=> ["a", "b", "c"]
>> a.unshift(a.delete('c'))
=> ["c", "a", "b"]
Is there a more legible way placing an element to the front of an array?
Edit my actual code:
if #admin_users.include?(current_user)
#admin_users.unshift(#admin_users.delete(current_user))
end
Maybe this looks better to you:
a.insert(0, a.delete('c'))
Maybe Array#rotate would work for you:
['a', 'b', 'c'].rotate(-1)
#=> ["c", "a", "b"]
This is a trickier problem than it seems. I defined the following tests:
describe Array do
describe '.promote' do
subject(:array) { [1, 2, 3] }
it { expect(array.promote(2)).to eq [2, 1, 3] }
it { expect(array.promote(3)).to eq [3, 1, 2] }
it { expect(array.promote(4)).to eq [1, 2, 3] }
it { expect((array + array).promote(2)).to eq [2, 1, 3, 1, 2, 3] }
end
end
sort_by proposed by #Duopixel is elegant but produces [3, 2, 1] for the second test.
class Array
def promote(promoted_element)
sort_by { |element| element == promoted_element ? 0 : 1 }
end
end
#tadman uses delete, but this deletes all matching elements, so the output of the fourth test is [2, 1, 3, 1, 3].
class Array
def promote(promoted_element)
if (found = delete(promoted_element))
unshift(found)
end
self
end
end
I tried using:
class Array
def promote(promoted_element)
return self unless (found = delete_at(find_index(promoted_element)))
unshift(found)
end
end
But that failed the third test because delete_at can't handle nil. Finally, I settled on:
class Array
def promote(promoted_element)
return self unless (found_index = find_index(promoted_element))
unshift(delete_at(found_index))
end
end
Who knew a simple idea like promote could be so tricky?
Adding my two cents:
array.select{ |item| <condition> } | array
Pros:
Can move multiple items to front of array
Cons:
This will remove all duplicates unless it's the desired outcome.
Example - Move all odd numbers to the front (and make array unique):
data = [1, 2, 3, 4, 3, 5, 1]
data.select{ |item| item.odd? } | data
# Short version:
data.select(&:odd?) | data
Result:
[1, 3, 5, 2, 4]
Another way:
a = [1, 2, 3, 4]
b = 3
[b] + (a - [b])
=> [3, 1, 2, 4]
If by "elegant" you mean more readable even at the expense of being non-standard, you could always write your own method that enhances Array:
class Array
def promote(value)
if (found = delete(value))
unshift(found)
end
self
end
end
a = %w[ a b c ]
a.promote('c')
# => ["c", "a", "b"]
a.promote('x')
# => ["c", "a", "b"]
Keep in mind this would only reposition a single instance of a value. If there are several in the array, subsequent ones would probably not be moved until the first is removed.
In the end I considered this the most readable alternative to moving an element to the front:
if #admin_users.include?(current_user)
#admin_users.sort_by{|admin| admin == current_user ? 0 : 1}
end
If all the elements in the array are unique you can use array arithmetic:
> a = ['a', 'b', 'c']
=> ["a", "b", "c"]
> a -= "c"
=> ["a", "b"]
> a = ["c"] + a
=> ["c", "a", "b"]
Building on above:
class Array
def promote(*promoted)
self - (tail = self - promoted) + tail
end
end
[1,2,3,4].promote(5)
=> [1, 2, 3, 4]
[1,2,3,4].promote(4)
=> [4, 1, 2, 3]
[1,2,3,4].promote(2,4)
=> [2, 4, 1, 3]

How to quickly print Ruby hashes in a table format?

Is there a way to quickly print a ruby hash in a table format into a file?
Such as:
keyA keyB keyC ...
123 234 345
125 347
4456
...
where the values of the hash are arrays of different sizes. Or is using a double loop the only way?
Thanks
Try this gem I wrote (prints hashes, ruby objects, ActiveRecord objects in tables): http://github.com/arches/table_print
Here's a version of steenslag's that works when the arrays aren't the same size:
size = h.values.max_by { |a| a.length }.length
m = h.values.map { |a| a += [nil] * (size - a.length) }.transpose.insert(0, h.keys)
nil seems like a reasonable placeholder for missing values but you can, of course, use whatever makes sense.
For example:
>> h = {:a => [1, 2, 3], :b => [4, 5, 6, 7, 8], :c => [9]}
>> size = h.values.max_by { |a| a.length }.length
>> m = h.values.map { |a| a += [nil] * (size - a.length) }.transpose.insert(0, h.keys)
=> [[:a, :b, :c], [1, 4, 9], [2, 5, nil], [3, 6, nil], [nil, 7, nil], [nil, 8, nil]]
>> m.each { |r| puts r.map { |x| x.nil?? '' : x }.inspect }
[:a, :b, :c]
[ 1, 4, 9]
[ 2, 5, ""]
[ 3, 6, ""]
["", 7, ""]
["", 8, ""]
h = {:a => [1, 2, 3], :b => [4, 5, 6], :c => [7, 8, 9]}
p h.values.transpose.insert(0, h.keys)
# [[:a, :b, :c], [1, 4, 7], [2, 5, 8], [3, 6, 9]]
No, there's no built-in function. Here's a code that would format it as you want it:
data = { :keyA => [123, 125, 4456], :keyB => [234000], :keyC => [345, 347] }
length = data.values.max_by{ |v| v.length }.length
widths = {}
data.keys.each do |key|
widths[key] = 5 # minimum column width
# longest string len of values
val_len = data[key].max_by{ |v| v.to_s.length }.to_s.length
widths[key] = (val_len > widths[key]) ? val_len : widths[key]
# length of key
widths[key] = (key.to_s.length > widths[key]) ? key.to_s.length : widths[key]
end
result = ""
data.keys.each {|key| result += key.to_s.ljust(widths[key]) + " " }
result += "\n"
for i in 0.upto(length)
data.keys.each { |key| result += data[key][i].to_s.ljust(widths[key]) + " " }
result += "\n"
end
# TODO write result to file...
Any comments and edits to refine the answer are very welcome.

Ruby: 1.8.7: How do I find the indexes in an array where elements are not nil?

my_array = [0, 1, 2, nil, nil, 3, nil, 4, nil]
should return [0,1,2,5,7]
via #the tin man: state.map.with_index { |e, i| (e.nil?) ? nil : i }.compact
unfortunately, that only works with 1.9
This gets there with v1.9.2:
my_array.map.with_index{ |e,i| (e.nil?) ? i : nil }.compact
=> [3, 4, 6, 8]
The question changed while I was answering, so this matches the question as it stands now:
my_array.map.with_index{ |e,i| (e.nil?) ? nil : i }.compact
=> [0, 1, 2, 5, 7]
It's just a case of switching the ternary operator's values around.
And, once again, the question changed. With 1.8.7 and 1.9.2:
ruby-1.8.7-p330 :004 > my_array.each_with_index.map{|e,i| (e.nil?) ? nil : i }.compact
=> [0, 1, 2, 5, 7]
ruby-1.9.2-p136 :002 > my_array.each_with_index.map{|e,i| (e.nil?) ? nil : i }.compact
=> [0, 1, 2, 5, 7]
I'm sure there's a quicker way, but:
result = []
my_array.each_with_index do |item, index|
result << index unless item.nil?
end

How to return a part of an array in Ruby?

With a list in Python I can return a part of it using the following code:
foo = [1,2,3,4,5,6]
bar = [10,20,30,40,50,60]
half = len(foo) / 2
foobar = foo[:half] + bar[half:]
Since Ruby does everything in arrays I wonder if there is something similar to that.
Yes, Ruby has very similar array-slicing syntax to Python. Here is the ri documentation for the array index method:
--------------------------------------------------------------- Array#[]
array[index] -> obj or nil
array[start, length] -> an_array or nil
array[range] -> an_array or nil
array.slice(index) -> obj or nil
array.slice(start, length) -> an_array or nil
array.slice(range) -> an_array or nil
------------------------------------------------------------------------
Element Reference---Returns the element at index, or returns a
subarray starting at start and continuing for length elements, or
returns a subarray specified by range. Negative indices count
backward from the end of the array (-1 is the last element).
Returns nil if the index (or starting index) are out of range.
a = [ "a", "b", "c", "d", "e" ]
a[2] + a[0] + a[1] #=> "cab"
a[6] #=> nil
a[1, 2] #=> [ "b", "c" ]
a[1..3] #=> [ "b", "c", "d" ]
a[4..7] #=> [ "e" ]
a[6..10] #=> nil
a[-3, 3] #=> [ "c", "d", "e" ]
# special cases
a[5] #=> nil
a[6, 1] #=> nil
a[5, 1] #=> []
a[5..10] #=> []
If you want to split/cut the array on an index i,
arr = arr.drop(i)
> arr = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
> arr.drop(2)
=> [3, 4, 5]
You can use slice() for this:
>> foo = [1,2,3,4,5,6]
=> [1, 2, 3, 4, 5, 6]
>> bar = [10,20,30,40,50,60]
=> [10, 20, 30, 40, 50, 60]
>> half = foo.length / 2
=> 3
>> foobar = foo.slice(0, half) + bar.slice(half, foo.length)
=> [1, 2, 3, 40, 50, 60]
By the way, to the best of my knowledge, Python "lists" are just efficiently implemented dynamically growing arrays. Insertion at the beginning is in O(n), insertion at the end is amortized O(1), random access is O(1).
Ruby 2.6 Beginless/Endless Ranges
(..1)
# or
(...1)
(1..)
# or
(1...)
[1,2,3,4,5,6][..3]
=> [1, 2, 3, 4]
[1,2,3,4,5,6][...3]
=> [1, 2, 3]
ROLES = %w[superadmin manager admin contact user]
ROLES[ROLES.index('admin')..]
=> ["admin", "contact", "user"]
another way is to use the range method
foo = [1,2,3,4,5,6]
bar = [10,20,30,40,50,60]
a = foo[0...3]
b = bar[3...6]
print a + b
=> [1, 2, 3, 40, 50 , 60]
I like ranges for this:
def first_half(list)
list[0...(list.length / 2)]
end
def last_half(list)
list[(list.length / 2)..list.length]
end
However, be very careful about whether the endpoint is included in your range. This becomes critical on an odd-length list where you need to choose where you're going to break the middle. Otherwise you'll end up double-counting the middle element.
The above example will consistently put the middle element in the last half.

Resources