如何过滤对象数组

时间:2018-07-30 21:08:30

标签: ruby ruby-on-rails-4

我有以下对象数组

{
  : items=>[
    {
      : id=>"cam-id-1",
      : translations=>[
        {
          : name=>"abcd",
          : description=>"geiajfe",
          : locale=>: fr,
          : createdAt=>Fri,
          27Jul201800: 00: 00UTC+00: 00,
          : updatedAt=>Fri,
          27Jul201800: 00: 00UTC+00: 00
        },
        {
          : name=>"bon jor",
          : description=>"jwi nifneaoin ofieafi",
          : locale=>: de,
          : createdAt=>Fri,
          27Jul201800: 00: 00UTC+00: 00,
          : updatedAt=>Fri,
          27Jul201800: 00: 00UTC+00: 00
        },
        {
          : name=>"hello",
          : description=>"hello abcd",
          : locale=>: en,
          : createdAt=>Fri,
          27Jul201800: 00: 00UTC+00: 00,
          : updatedAt=>Fri,
          27Jul201800: 00: 00UTC+00: 00
        }
      ]
    },
    {
      : id=>"cam-id-2",
      : translations=>[

      ]
    }
  ],
}

我想根据语言环境过滤翻译数组。因此,例如,如果我通过locale = fr,则返回整个对象,但在翻译中仅返回一个对象,因为只有一个语言环境fr。

所以输出将是

 {
      : items=>[
        {
          : id=>"cam-id-1",
          : translations=>[
            {
              : name=>"abcd",
              : description=>"geiajfe",
              : locale=>: fr,
              : createdAt=>Fri,
              27Jul201800: 00: 00UTC+00: 00,
              : updatedAt=>Fri,
              27Jul201800: 00: 00UTC+00: 00
            }
          ]
        },
        {
          : id=>"cam-id-2",
          : translations=>[

          ]
        }
      ],
    }

我创建了一种过滤翻译的方法,但它只返回过滤后的翻译,我认为这不是正确的方法。

  def filter_translations(test)
    array = []
    test[:items][0][:translations].each do |t|
      array << t if t[:locale].to_s.casecmp(locale.to_s).zero? || t[:locale].to_s.include?(locale.to_s)
    end
    array
  end

有什么主意吗?

1 个答案:

答案 0 :(得分:0)

要从列表中仅获取单个内容,请使用first

# {:name=>"abcd", :description=>"geiajfe", :locale=>:fr}
translation_fr = item.fetch(:translations).first { |translation|
  translation[:locale] == :fr
}

要获取所有项目的内容,请将其包装在map中。

# [ {:name=>"abcd", :description=>"geiajfe", :locale=>:fr}, ... ]
items_fr = test.fetch(:items).map { |item|
  item.fetch(:translations).first { |translation|
    translation[:locale] == :fr
  }
}

要返回项目+所选的翻译,您可以从map返回。但是您需要注意,item不是副本。如果您在map中进行了更改,则在tests中进行了更改。

例如...

items_fr = test.fetch(:items).map { |item|
  translation = item.fetch(:translations).first { |translation|
    translation[:locale] == :fr
  }
  item[:translations] = [translation]
  item
}

这将返回您想要的。

{:id=>"cam-id-1", :translations=>[{:name=>"abcd", :description=>"geiajfe", :locale=>:fr}]}

但是它也会更改test中的项目,因为它们是相同的对象。相反,您需要返回每个item的副本。您可以呼叫item = item.clone并更改密钥,也可以一步一步使用merge

items_fr = test.fetch(:items).map { |item|
  translation = item.fetch(:translations).first { |translation|
    translation[:locale] == :fr
  }
  item.merge translations: [translation]
}

建议您不要为选定的翻译添加新的item[:translations]键,而不是更改translation

# [ {:id=>"cam-id-1", :translations=>[{:name=>"abcd", :description=>"geiajfe", :locale=>:fr}, {:name=>"bon jor", :description=>"jwi nifneaoin ofieafi", :locale=>:de}, {:name=>"hello", :description=>"hello abcd", :locale=>:en}], :translation=>{:name=>"abcd", :description=>"geiajfe", :locale=>:fr}} ]
items_fr = test.fetch(:items).map { |item|
  translation = item.fetch(:translations).first { |translation|
    translation[:locale] == :fr
  }
  item[:translation] = translation
  item
}

现在,如果需要,您可以重复此过程以选择新的翻译。


您可以通过几种方式改进此过程。首先,如果可能,请将translation数组更改为在语言环境上键入的哈希值,以加快查找速度。其次,将所有内容放入封装细节的对象中,并允许更复杂的行为。

class Translations
  attr_accessor :translations

  def initialize
    @translations = {}
  end

  # Get the translation for a locale
  def for_locale(locale)
    translations.fetch(locale)
  end

  # Add a translation for a locale
  def add_translation(translation)
    translations[translation.locale] = translation
  end

  # Import translations from an array. Reorganize
  # them as a hash keyed on locale.
  def import_from_array(translations)
    translations.each do |import_translation|
      add_translation Translation.new(import_translation)
    end
  end
end

class Translation
  attr_accessor :name, :description, :locale

  def initialize(name:, description:, locale:)
    @name = name
    @description = description
    @locale = locale
  end
end

class Item
  attr_accessor :translations, :translation, :id

  def initialize
    @translations ||= Translations.new
  end

  def pick_translation(locale)
    @translation = translations.for_locale(locale)
  end

  def import_from_hash(**args)
    @id = args.fetch(:id)
    translations.import_from_array( args.fetch(:translations) )
  end
end

items = test.fetch(:items).map do |import|
  # parse
  item = Item.new.import_from_hash(import)
  # pick a translation
  item.pick_translation(:fr)
  # return the object
  item
end

例如,如果重复这些翻译并且副本占用了内存,则可以利用Flyweight pattern。他们可以共享相同的Translation对象,而不是每个Item都有自己的副本。


我使用的是Hash#fetch而不是[],因此如果缺少必需的键,它将抛出KeyError异常。