Ruby 加班中……求问一个问题 (递归效用方法,已解决)

siriuszhuang · 2012年07月05日 · 最后由 siriuszhuang 回复于 2012年07月13日 · 3531 次阅读

我有一个 hash 表,结构类似这样:

hash = {
  :name => [:username],
  :age => [:age],
  :girlfriend_count => [:girlfrend => [:count]]
}

想要实现的效果是让程序执行:

user = User.find 1
data = {}
hash.each do |key, value|
  # block
end

在 block 内实现下面功能:

data[:name] = user.username
data[:age] = user.age
data[:girlfriend_count] = user.girlfrend.count

不知道如何实现,eval 貌似不太优雅。求解。 谢谢大家

data['attribute'] = user.send('attribute') ???

#1 楼 @fresh_fish 不是吧?那这个怎么实现?

data[:girlfriend_count] = user.girlfrend.count
if 'attribute'.end_with?('_count')
  'attribute' = 'attribute'.delete('_count')
  data['attribute'] = user.send('attribute').send('count')
else
  data['attribute'] = user.send('attribute')
end

也比较锉

没有看懂需求。 data 的结构必须跟 hash 保持一致么?为什么不能在使用 data 的地方将 User.find 1 返回的对象直接传进去?反正所有的数据也是从 User 的实例中取得的嘛。

这个问题挺有意思的,我解决过类似的问题, 我建议使用 "递归"+send,解决这个问题

或者

if 'attribute' =~  /(.*)_count$/
  data[$1] = user.send($1.to_s).send('count')
else
 ...
end

#3 楼 @fresh_fish 看了一下 send,貌似可行。再看看有没有更优雅的吧。先谢谢@fresh_fish

#5 楼 @ery 我也是这么想的。正在 coding,写好了贴给大家~!Thanks all~

我曾经写过的一段代码 https://github.com/ery/showbuilder/blob/master/lib/showbuilder.rb 第 54 行


call_object_methods sale, [:customer, :name]

# same with sale.customer.name

def call_object_methods(object, methods)
  unless object
    return
  end

  methods = Array.wrap(methods)
  if methods.count == 0
    return
  end

  first_method = methods.first
  unless first_method
    return
  end

  unless object.respond_to?(first_method)
    return
  end

  method_result = object.send(first_method)
  if methods.count <= 1
    return method_result
  else
    remaining_methods = methods.clone
    remaining_methods.shift
    return call_object_methods(method_result, remaining_methods)
  end
end

https://github.com/intridea/hashie/ 不太懂题目。。。。 但操作哈希表什么的,我喜欢用 mash = Hashie::Mash.new(hash) 我现在的项目就在用

Hash[ hash.map{ |k, v| [k, v =~  /(.*)_count$/ ? user.send($1.to_s).send('count') : user.send(v) ] } ]

不知可行不

#11 楼 @fresh_fish 不限于#count 方法的,这里只是举一个例子。还是谢谢你~!

#11 楼 @fresh_fish

如果用 Model 的 delegate 方法去取得 user.girlfrend.count ,然后用你在一楼的 send 实现,我感觉足够简洁,优雅易读容易维护了

User < ActiveRecord::Base
  delegate :count, :to => :girlfrend, :prefix => true
end

data[:girlfriend_count] = user.send :girlfrend_count

最讨厌这种需求不清楚的问题了,如果实在不知道怎么描述可以这样描述:给出 input 值和想要得到的 output 值,还有不能使用的方法,和要求使用的方法等。

#11 楼 @fresh_fish #10 楼 @azhao 以下是我实现的方法。谢谢大家的支持~!

def call_object_methods(object, methods)
  return nil if object.nil?
  return object.send methods[0] if methods[0].is_a? Symbol

  methods.each do |method|
    method.each do |key, value|
      return call_object_methods((object.send key), value)
    end
  end
end
call_object_methods user, {
  :name => [:username],
  :age => [:age],
  :girlfriend_count => [:girlfrend => [:count]]
}

这么用的吗?

#17 楼 @ery 这么用的

hash = {
  :name => [:username],
  :age => [:age],
  :girlfriend_count => [:girlfrend => [:count]]
}
User.find_each(options) do |user|
  user_info = {}
  @@user_data_list.each do |key, value|
    if value.is_a? Array
      user_info[key] = call_object_methods user, value
    else
      user_info[key] = value
    end
  end
  data << user_info
end
需要 登录 后方可回复, 如果你还没有账号请 注册新账号