简体   繁体   English

ruby对象数组...或哈希

[英]ruby object array… or hash

I have an object now: 我现在有一个对象:

class Items
  attr_accessor :item_id, :name, :description, :rating

  def initialize(options = {})
      options.each {
        |k,v|
        self.send( "#{k.to_s}=".intern, v)
      }
  end

end

I have it being assigned as individual objects into an array... 我把它作为单个对象分配到一个数组中......

@result = []

some loop>>
   @result << Items.new(options[:name] => 'name', options[:description] => 'blah')
end loop>>

But instead of assigning my singular object to an array... how could I make the object itself a collection? 但不是将我的单个对象分配给数组......我怎样才能使对象本身成为一个集合?

Basically want to have the object in such a way so that I can define methods such as 基本上想要以这样的方式拥有对象,以便我可以定义诸如的方法

def self.names
   @items.each do |item|
      item.name
   end
end

I hope that makes sense, possibly I am overlooking some grand scheme that would make my life infinitely easier in 2 lines. 我希望这是有道理的,可能我忽视了一些宏伟的计划,这将使我的生活在两行中变得无比轻松。

A few observations before I post an example of how to rework that. 在我发布一个如何重做的例子之前的一些观察。

  • Giving a class a plural name can lead to a lot of semantic issues when declaring new objects, as in this case you'd call Items.new, implying you're creating several items when in fact actually making one. 给一个复数名称的类在声明新对象时会导致很多语义问题,因为在这种情况下你会调用Items.new,这意味着你实际创建了几个项目。 Use the singular form for individual entities. 对单个实体使用单数形式。
  • Be careful when calling arbitrary methods, as you'll throw an exception on any misses. 调用任意方法时要小心,因为你会在任何未命中时抛出异常。 Either check you can call them first, or rescue from the inevitable disaster where applicable. 要么检查你是否可以先打电话给他们,或者从适用的不可避免的灾难中解救出来。

One way to approach your problem is to make a custom collection class specifically for Item objects where it can give you the information you need on names and such. 解决问题的一种方法是专门为Item对象创建一个自定义集合类,它可以为您提供名称等所需的信息。 For example: 例如:

class Item
  attr_accessor :item_id, :name, :description, :rating

  def initialize(options = { })
    options.each do |k,v|
      method = :"#{k}="

      # Check that the method call is valid before making it
      if (respond_to?(method))
        self.send(method, v)
      else
        # If not, produce a meaningful error
        raise "Unknown attribute #{k}"
      end
    end
  end
end

class ItemsCollection < Array
  # This collection does everything an Array does, plus
  # you can add utility methods like names.

  def names
    collect do |i|
      i.name
    end
  end
end

# Example

# Create a custom collection
items = ItemsCollection.new

# Build a few basic examples
[
  {
    :item_id => 1,
    :name => 'Fastball',
    :description => 'Faster than a slowball',
    :rating => 2
  },
  {
    :item_id => 2,
    :name => 'Jack of Nines',
    :description => 'Hypothetical playing card',
    :rating => 3
  },
  {
    :item_id => 3,
    :name => 'Ruby Book',
    :description => 'A book made entirely of precious gems',
    :rating => 1
  }
].each do |example|
  items << Item.new(example)
end

puts items.names.join(', ')
# => Fastball, Jack of Nines, Ruby Book

Do you know the Ruby key word yield ? 你知道Ruby关键词的产量吗?

I'm not quite sure what exactly you want to do. 我不太确定你到底想做什么。 I have two interpretations of your intentions, so I give an example that makes two completely different things, one of them hopefully answering your question: 我对你的意图有两种解释,所以我给出了一个例子,它使两个完全不同的东西,其中一个希望回答你的问题:

class Items
  @items = []
  class << self
    attr_accessor :items
  end
  attr_accessor :name, :description
  def self.each(&args)
    @items.each(&args)
  end
  def initialize(name, description)
    @name, @description = name, description
    Items.items << self
  end
  def each(&block)
    yield name
    yield description
  end
end

a = Items.new('mug', 'a big cup')
b = Items.new('cup', 'a small mug')
Items.each {|x| puts x.name}
puts
a.each {|x| puts x}

This outputs 这输出

mug
cup

mug
a big cup

Did you ask for something like Items.each or a.each or for something completely different? 您是否要求像Items.eacha.each这样的东西或者完全不同的东西?

Answering just the additional question you asked in your comment to tadman's solution: If you replace in tadman's code the definition of the method names in the class ItemsCollection by 回答你在评论中对tadman解决方案提出的其他问题:如果你在tadman的代码中替换了类ItemsCollection中方法名称的定义

def method_missing(symbol_s, *arguments)
  symbol, s = symbol_s.to_s[0..-2], symbol_s.to_s[-1..-1]
  if s == 's' and arguments.empty?
    select do |i|
      i.respond_to?(symbol) && i.instance_variables.include?("@#{symbol}")
    end.map {|i| i.send(symbol)}
  else
    super
  end
end

For his example data you will get following outputs: 对于他的示例数据,您将获得以下输出:

puts items.names.join(', ')
# => Fastball, Jack of Nines, Ruby Book
puts items.descriptions.join(', ')
# => Faster than a slowball, Hypothetical playing card, A book made entirely of precious gems

As I don't know about any way to check if a method name comes from an attribute or from another method (except you redefine attr_accessor, attr, etc in the class Module) I added some sanity checks: I test if the corresponding method and an instance variable of this name exist. 因为我不知道有什么方法来检查一个方法名是来自一个属性还是来自另一个方法(除了你在类Module中重新定义attr_accessor,attr等)我添加了一些健全性检查:我测试是否相应的方法和存在此名称的实例变量。 As the class ItemsCollection does not enforce that only objects of class Item are added, I select only the elements fulfilling both checks. 由于类ItemsCollection不强制只添加类Item的对象,因此我只选择满足两个检查的元素。 You can also remove the select and put the test into the map and return nil if the checks fail. 您还可以删除选择并将测试放入地图中,如果检查失败,则返回nil。

The key is the return value. 关键是返回值。 If not 'return' statement is given, the result of the last statement is returned. 如果没有给出'return'语句,则返回最后一个语句的结果。 You last statement returns a Hash. 你最后一个语句返回一个哈希。

Add 'return self' as the last line of initialize and you're golden. 添加'return self'作为初始化的最后一行,你就是黄金。

Class Item
  def initialize(options = {})
    ## Do all kinds of stuff. 

    return self
  end
end

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM