4

So the standard way to write this seems to be array.include?(object). However I find that much harder to read and understand quickly compared to object.in(array). Is there anything like this in ruby?

The example I just hit this again was (user_role is a string, and allowed_user_roles is an array of strings):

allowed_user_roles.include?(user_role)

I know its probably personal preference, but I find this so much easier to read and comprehend.

user_role.in(allowed_user_roles)
mrzasa
  • 22,895
  • 11
  • 56
  • 94
Andrew
  • 2,829
  • 3
  • 22
  • 19
  • 2
    A collection contains references to the objects it holds, not vice-versa. An object usually isn't aware that it is included somewhere. You therefore say `ary.index(obj)`, not `obj.index_within(ary)`. Or `'ruby'.start_with?('ru')` instead of `'ru'.prefix_of?('ruby')`. In your example, the array probably contains another string with the same characters, i.e. not the same object. So you're asking the string _"hey string, is there another string in this array that looks just like you?"_ instead of _"hey array, do you include a string equal to this one?"_. To me, the former seems a bit odd. – Stefan Jul 05 '19 at 15:36
  • 2
    It's a bit odd, but it reads great: `if str.in? names then... ` :) – mrzasa Jul 05 '19 at 15:39
  • I just find it way more readable and english like with the object.in?(array). Especially when in an if statement, I find if object.in?(array) way easier to understand than if array.include?(object). Just personal preference though really. It also depends on the context as well probably. – Andrew Jul 05 '19 at 16:19
  • @mrzasa, ...or `if str.in? name then...`. – Cary Swoveland Jul 05 '19 at 16:44
  • 1
    @Andrew it looks really nice. But to me, something like `needle.in?(haystack)` implies that needle knows whether it's inside the haystack. It just has to say _yes_ or _no_, i.e. I'd expect an instant response. Whereas `haystack.include?(needle)` already sounds like a tedious task. It would be a different story without a receiver. If `in?` was an operator then `obj in? collection` (no dot, not directed to `obj`) would be more of a general question and could be syntactic sugar for `collection.include?(obj)`. As you said, it's probably personal preference. – Stefan Jul 05 '19 at 16:46
  • I dont find it, but I thought Rails had an implementation of this. I thought about the same issue lately and was thinking about writing a Feature Request for Ruby. However, it is not trivial to define proper results (what should `[1,2].in?([1,3,4])` return?) and I understand @Stefan s line of thought. – Felix Jul 06 '19 at 07:27

2 Answers2

6

It's not in core Ruby, but it's added in ActiveSupport core extensions. If you use Rails, you have that available:

1.in?([1,2])        # => true
"lo".in?("hello")   # => true
25.in?(30..50)      # => false
1.in?(1)            # => ArgumentError

To use it outside of Rails, you need to install active_support gem and then require active_support/core_ext/object/inclusion:

# Gemfile
gem 'active_support'

# code
require 'active_support/core_ext/object/inclusion'
mrzasa
  • 22,895
  • 11
  • 56
  • 94
  • 1
    Awesome thanks, I'm using rails, so this is exactly what I was looking for. I think I might have even used this in? method in years past, but after taking a year long break from rails last year I had forgotten about this. – Andrew Jul 05 '19 at 16:14
  • pls consider upvoting&accepting if it's helpful :) – mrzasa Jul 05 '19 at 16:16
  • It's worth reading through ActiveSupport to see what Ruby code Rails is refining/extending. Rails refines core Ruby code so much that it is often difficult to distinguish the two. – Matthew Jul 05 '19 at 18:12
  • This was one of my first questions on stackoverflow. :) – Sergio Tulentsev Sep 18 '19 at 20:19
  • 1
    @Matthew There is a quiz that shows how hard it is to distinguish vanilla Ruby form Rails-flavored Ruby: https://railshurts.com/quiz/ – mrzasa Sep 18 '19 at 20:21
  • @mrzasa very nice :) – Matthew Sep 19 '19 at 08:32
2

As an experiement, you could also create this yourself (although monkeypatching is generally frowned on)

class Object
  def in?(collection)
    raise ArgumentError unless collection.respond_to?(:include?)
    collection.include?(self)
  end
end

Then anything inheriting from Object (which is pretty much anything) will have an #in? method.

5.in?(0..10)
=> true

'carrot'.in?(['potato', 'carrot', 'lettuce'])
=> true
SteveTurczyn
  • 36,057
  • 6
  • 41
  • 53
  • "Refining" objects is typically safer and more accepted (Rails uses refinements in ActiveSupport). Although, this is still technically monkeypatching. – Matthew Jul 05 '19 at 18:14