Project

General

Profile

Actions

Feature #16435

closed

Array#to_proc

Added by zverok (Victor Shepelev) almost 2 years ago. Updated over 1 year ago.

Status:
Rejected
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:96338]

Description

The idea is obvious, but I couldn't find it discussed anywhere on tracker before. Please point me at the previous discussions if any.

class Array
  def to_proc
    proc { |v| v.dig(*self) }
  end
end
# Or, alternatively, see about alternatives at the end of proposal:
class Array
  def to_proc
    proc { |v| v[*self] }
  end
end

The implementation seems to provide clean and unambiguous collections indexing in Enumerators:

# Basic objects data, which could be obtained from JSON, CSV, Database...
data = [
  {name: 'John', department: {id: 1, title: 'Engineering'}, salary: 1000}, 
  {name: 'Jane', department: {id: 1, title: 'Engineering'}, salary: 1200},
  {name: 'Boris', department: {id: 2, title: 'Accounting'}, salary: 800},
  {name: 'Alice', department: {id: 3, title: 'Management'}, salary: 1500}
]
data.map(&[:name])
# => ["John", "Jane", "Boris", "Alice"] 
data.min_by(&[:salary])
# => {:name=>"Boris", :department=>{:id=>2, :title=>"Accounting"}, :salary=>800} 
pp data.group_by(&[:department, :title])
# {"Engineering"=>
#   [{:name=>"John",
#     :department=>{:id=>1, :title=>"Engineering"},
#     :salary=>1000},
#    {:name=>"Jane",
#     :department=>{:id=>1, :title=>"Engineering"},
#     :salary=>1200}],
#  "Accounting"=>
#   [{:name=>"Boris",
#     :department=>{:id=>2, :title=>"Accounting"},
#     :salary=>800}],
#  "Management"=>
#   [{:name=>"Alice",
#     :department=>{:id=>3, :title=>"Management"},
#     :salary=>1500}]}

# Works with arrays, too:
data.map(&:values).map(&[0])
# => ["John", "Jane", "Boris", "Alice"]

# And with mixes:
data.group_by(&[:department, :title]).values.map(&[0, :name]) 
# => ["John", "Boris", "Alice"]

Naked structured data seems to be a common enough thing to make working with them easier.

Some prior info:

  • Googling it around, I found the idea was first invented back in 2014, and another one in 2015, not sure if it was proposed on the tracker.
  • Other proposals for Array#to_proc was: to call several methods in sequence 1, 2, and to call method with argument 1, 2, 3, to call several methods in parallel: 1

Honestly, I feel that proposed usage is the most frequently needed.

Also, the readability of the version seems more or less straightforward:

# Existing shortcut, for example:
data.map(&:keys)
# Is equivalent to
data.map { |x| x.keys }
#          ^^^^^ -- "just remove this part"

# Proposed shortcut:
data.map(&[:name])
# Is equivalent to
data.map { |x| x[:name] }
#          ^^^^^ -- "just remove this part"

dig or [] alternative implementations

It is up to discussion (if the whole idea holds water) whether dig should be used or just []. The dig version is convenient for nested structures but slightly breaks "equivalency" shown above, and just [] version will allow this:

data.map(&:values).map(&[1..-1])
# => [[{:id=>1, :title=>"Engineering"}, 1000], [{:id=>1, :title=>"Engineering"}, 1200], [{:id=>2, :title=>"Accounting"}, 800], [{:id=>3, :title=>"Management"}, 1500]]

Maybe, for the sake of explainability, "just []" should be preferred, with digging performed by other means.

Actions

Also available in: Atom PDF