如何在ruby中链接取消引用?

如何在ruby中链接取消引用?,ruby,Ruby,我想要一个方法t.find_first[:a,:b,:c],:d,:e]用于散列t,它返回第一组解引用的键 对于上面的示例,它将相当于t.try(:[],:a).try(:[],:b).try(:[],:c)| | t.try(:[],:d).try(:[],:e) 目前我在: def find_first t, *keysets keysets.each do |keys| val = val || keys.inject(t){ |h, key| h.try(:[], key)

我想要一个方法
t.find_first[:a,:b,:c],:d,:e]
用于散列
t
,它返回第一组解引用的键

对于上面的示例,它将相当于
t.try(:[],:a).try(:[],:b).try(:[],:c)| | t.try(:[],:d).try(:[],:e)

目前我在:

def find_first t, *keysets
  keysets.each do |keys|
    val = val || keys.inject(t){ |h, key| h.try(:[], key) }
  end
end

我想不是特别优雅,但有时候少施魔法更好

class FindFirst
  def self.find_first(t, *keysets)
    keysets.each do |keyset|
      return keyset if can_dereference_chain?(t, keyset)
    end
    nil
  end

  def self.can_dereference_chain?(t, keyset)
    keys = keyset.dup
    key = keys.shift
    if value = t[key]
      keys.empty? ? true : can_dereference_chain?(value, keys)
    else
      false
    end
  end
end

RSpec.describe FindFirst do
  subject { FindFirst }
  let(:t) { {a:{b:{c:1}}} }
  let(:keyset_1) { [:a, :b, :c] }
  let(:keyset_2) { [:a, :b, :d] }

  describe "when first keyset works" do
    it "returns the first keyset" do
      expect(subject.find_first(t, keyset_1)).to eq keyset_1
      expect(subject.find_first(t, keyset_1, keyset_2)).to eq keyset_1
    end
  end

  describe "when no keysets work" do
    it "returns nil" do
      expect(subject.find_first(t, keyset_2)).to eq nil
      expect(subject.find_first(t, [])).to eq nil
      expect(subject.find_first({}, keyset_1, keyset_2, [])).to eq nil
    end
  end

  describe "when first keyset does not work" do
    describe "and second keyset does" do
      it "returns the second keyset" do
        expect(subject.find_first(t, keyset_2, keyset_1)).to eq keyset_1
      end
    end
  end
end

没有理由为此创建特殊方法

Ruby<2.3

value   = t.try(:[], :a).try(:[], :b).try(:[], :c)
value ||= t.try(:[], :d).try(:[], :e)
Ruby>=2.3:

value   = t&.[](:a)&.[](:b)&.[](:c)
value ||= t&.[](:d)&.[](:e)
虽然这个问题是间接提出的,但从方法签名来看,您可能试图进入一个嵌套的散列,就像params对象一样

如果是这样的话,你应该看看这个。如果你不在2.3上,你可以随时把它修补好

value   = t.dig(:a, :b, :c)
value ||= t.dig(:d, :e)
不使用
散列#dig
进入散列的另一种模式如下:

params.fetch(:a, {}).fetch(:b, {})[:c]

你有没有尝试过把它写得更有程序性?您的长格式版本可能会被折叠成一系列在组中迭代的调用。我使用
inject
尝试了一些东西,但看起来不对。@将发布您尝试过的任何内容。通常情况下,解决方案就在你能得到的范围内,你只需要在正确的方向上轻推一下就可以了<代码>挖掘看起来非常有用。@只需根据需要编写任意数量的
| |=
作业即可。此外,
Array#dig
也存在。
h = { a: :d, c: :f, d: :e, f: :a }
possible_sequences = [[:a, :b, :c], [:d, :e, :f], [:a, :d, :e]]

possible_sequences.find { |a| a[1..-1].reduce(a[0]) { |t,k| k if (t && h[t]==k) } }
  #=> [:a, :d, :e]