【问题标题】:I need a better way to query an array我需要一种更好的方法来查询数组
【发布时间】:2016-02-02 14:56:52
【问题描述】:

我有一个与可扩展新闻 API 链接的 T 恤应用程序。我有一个可行的解决方案,但必须有更好的方法。该 api 的产品具有一系列颜色,颜色具有一系列图像。我想显示“正面”的图像,但数组中的第一个图像并不总是前面。我的视力很窄,因为我想不出比这更好的方法来查询数组中的标签“Front”......

查看:

<% if @product['colors'][0]['images'][0]['label'] =='Front' %>
  <%= image_tag @product['colors'][0]['images'][0]['url'] %>
<% elsif @product['colors'][0]['images'][1]['label'] =='Front' %>
  <%= image_tag @product['colors'][0]['images'][1]['url'] %>
<% elsif @product['colors'][0]['images'][2]['label'] =='Front' %>
  <%= image_tag @product['colors'][0]['images'][2]['url'] %>
<% elsif @product['colors'][0]['images'][3]['label'] =='Front' %>
  <%= image_tag @product['colors'][0]['images'][3]['url'] %>
<% end %>

控制器:

def show_product
  @product = scalable_press.show_product(params[:product])
end

有没有更好的方法来做到这一点?

【问题讨论】:

  • 当我看到这样的代码时,只有一个问题要问:你是怎么得到这个数据结构的? @product 是什么,为什么 colours 属性包含的任何内容都有图像属性?
  • 他说,是因为一个API。有时您无法控制返回的内容,
  • @Leito - 错过了那部分。我同意您无法控制返回的内容,但您可以控制如何处理它。如果它来自 json,那么创建一个负责将 json 转换为可用对象的类就很容易了。那么上面的代码就只会读取@product.colors.first.front_image_url,没有任何不必要的条件,都是分类的,易于阅读。
  • 是的,我无法控制 api 返回的内容。我讨厌它,但由于某种原因,我无法提出更好的解决方案。每种颜色有 4 张图像,正面、背面、左袖和右袖。图像数组具有 URL 和标签。我发现他们的 API 有点草率,但他们打印的衬衫质量非常好,而且手动输入订单开始花费我太长时间。

标签: ruby-on-rails arrays ruby json httparty


【解决方案1】:

使用find:

<% item = @product['colors'][0]['images'].find{|i| i['label'] == 'Front'} %>
<%= image_tag item['url'] if item.present? %>

【讨论】:

    【解决方案2】:

    每次我必须处理我无法控制的数据结构时,我都会创建一个类来将该数据结构包装成我自己的喜好。我在上面看到的最大问题是你将 API 结构与你的视图相关联,想象一下一旦 API 发生变化就重构它。

    module <ExternalAPIName>
      IMAGE_TYPES = {
        front: "Front"
        ...
      }
      class Product
        attr_reader :colors
    
        def self.get(product)
          # Depending on what scalable_press is you can either call it directly or declare module attribute
          new scalable_press.show_product(product)
        end
    
        def initialize(data_hash)
          parse_colors(data_hash['colors'])
        end
    
        private
    
        def parse_colors(colors)
          @colors = colors.map {|color_hash| Color.new(color_hash) }
        end
      end
    
      class Color
    
        def initialize(color_hash)
          parse_images color_hash['images']
        end
    
        IMAGE_TYPES.each do |name, label|
          define_method "#{name}_image" do
            @images.find {|image| image.type == name }
          end
        end 
    
        private
    
        def parse_images(images)
          @images = images.map {|image_hash| Image.new(image_hash) }
        end
      end
    
      class Image
        attr_reader :url, :type
    
        def initialize(data_hash)
          @url = data_hash['url']
          @type = IMAGE_TYPES.key(data_hash['label'])
        end
      end
    end
    

    相当多的代码,我同意,但它在很大程度上简化了您的其余代码:

    控制器:

    @product = ExternalAPIName.get(params[:product])
    

    查看:

    <%= image_tag(@product.colors.first.front_image.url) %>
    

    它为您提供了对收到的数据的完全自定义能力,最重要的是,您将嵌套的哈希结构转换为属于您的应用程序域的对象。您现在可以将任何过滤、搜索和数据操作方法挂接到这些对象中。

    【讨论】:

      【解决方案3】:

      您可以创建一个方便的地图:

      image_map = @product['colors'][0]['images'].each_with_object({}) do |h,obj| 
        obj[h["label"]] = h["url"]
      ebd
      

      现在,您可以访问各种图像 URL:

      image_map["Right"]
      #=> "http://i1.ooshirts.com/images/lab_shirts/Kiwi-5-R.jpg"
      
      image_map["Left"]
      #=> "http://i1.ooshirts.com/images/lab_shirts/Kiwi-5-L.jpg"
      

      我正在使用来自here 的示例 JSON,理想情况下,您应该添加一个指向它的链接。

      【讨论】:

        猜你喜欢
        • 2021-04-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多