Understanding the code functionality

Hello everyone!
I’m new to Ruby and I’m trying to understand a program one of my colleagues has written. To be more precise, I stumbled upon this:

failed_hosts.reject!{ |h| ca_hosts_down.any? { |c| (c[:host] == h[:host] && c[:binary] == h[:binary]) } }
(failed_hosts & ca_hosts_down are arrays)

Do I understand this correctly, that these are some kind of two cycles, which remove elements from failed_hosts that have the same host and binary parameters as any element from ca_hosts_down?

Thanks in advance!

It sounds like failed_hosts and ca_hosts_down are just hashes. Hashes hold key and value pairs. The keys are always unique. If you store a duplicate key, you will see the previous value is discarded. Also, hashes are not for storing sequential data like Arrays. Here’s a sample hash:

a = {:hello => :hello.class, 5 => 5.class, true => true.class, 'hello' => 'hello'.class, ?A => ?A.class, x: :x.class}
p a.values                 # => [5, Integer, TrueClass, String, String, Symbol]
p a[:hello]                 # => Symbol
p a[true]                   # => TrueClass
p a[100.*(0).zero?]    # => TrueClass    # because 100 * 0 == 0

You can store any data in a hash and array. Both arrays and hashes are mutable objects. But rubyists prefer hash when to need to look up for an item - it’s optimized for that.
For sequential storage of duplicate data - it’s array that’s preferred. Arrays can iterate over items faster than hashes too!

.reject and the .any? are just methods on the hash.
There are two kind of blocks, the low precedence do ... end block and the high precedence { } block.

Every method can take a block in Ruby even if it’s not used.

def foo(&block)
    block.call(1)
end

def bar() yield 1 end
def baz() 5 end

foo { |y| puts y }     # output: 1 ; returns: nil
bar { |y| puts y }     # output: 1 ; returns: nil
bar { |y| p y + y }    # output: 2 returns: 2
baz { |y| puts y }     # output: none, returns: 5

Blocks take argument in ||. The |h| contains a value yielded by the reject! method. The |c| contains value yielded by the any? method on each iteration.

Everything is an object in Ruby. The :host is a symbol. You can write :Hello or %s(Hello) to make a symbol called :Hello. Generally symbol objects are light weight, just like integer objects.

Speaking of reject!, generally the reject! is a part of Enumerator#reject!. It’s called the reject bang method because of the !. It modifies the original object without changing it’s object_id. In other words, the memory location of the object isn’t changed, and thus, unlike = assignment operator, it doesn’t copy the content of the hash from a memory location to another memory location and releasing the previous memory. Nevertheless, it’s way faster if you have a hash with millions of key - value pairs. There is also a reject method without bang !, which doesn’t modify the original hash.

BTW, here reject! rejects all items from the failed_hosts hash if ca_hosts_down hash has one or more item that matches the key :host and :binary at the same time.

Hope this helps!

Sorry, but I’m afraid that SouravGoswami answer is wrong.
So here is my version :slight_smile:

failed_hosts is array of Hash ( or array of object which respond to [] operator).
idem for ca_hosts.
By example,

failed_hosts= [
  {host: "aaa", binary: false , ...},
  {host: "bbb", binary: true, ... },
  ....
]

array.reject! suppress item off the array if bloc respond true with item parameter.
array.any? { } respond true of almost one item of the array apply to the bloc respond true.
So your understanding is good !

Oh that’s true. Yeah, I mean they both couldn’t be just a hash, otherwise you could write failed_hosts.reject!{ |k, v ... . And it’s 100% array because of the h[:host] and c[:binary]. So both failed_hosts and ca_hosts_down are arrays that contains hash objects. Thanks for pointing that out!