Day6. Array & Hash 之间的组合应用

Hash 在其他语言称为Object, Dictionary,但无论是在哪个程序语言中,HashArray 都为组成资料中两个很重要的元素。许多资料结构为不单单只是阵列,也不单单只是Hash,甚至有很多复杂的Hash搭配Array的结构等等。

今天,我们会仔细的介绍ArrayHash 之间的关系

Array to Hash

论阵列转Hash,最简单的方式就是先把资料型态为凑为俩俩一对,之後再to_h 便很容易

[%w(a b), %w(c d), %w(e f)].to_h   #=> {"a"=>"b", "c"=>"d", "e"=>"f"}
%w(a b c d e f).to_h               #=> 会坏掉

将上述的观念建立起来以後,我们来开始看其他转 Hash 的情境

⭐️ 自我配对

虽然我们不能写成 %w(item1 item2 item3 item4).to_h,但我们可以使用下列的方式转为hash

a = ["item 1", "item 2", "item 3", "item 4"]
h = Hash[*a] 
#=> { "item 1" => "item 2", "item 3" => "item 4" }

⭐️ 将阵列使用#zip作配对以後,转为杂凑

不仅想要将两个阵列凑成Hash,还希望当中的key 为符号的话,我们该怎麽做?以下提供三种转Hash的方法。

keys = %w[a b c d e]
values = %w(apple bear cat dog elephant)

Hash[keys.map(&:to_sym).zip(values)]
#=> {:a=>"apple", :b=>"bear", :c=>"cat", :d=>"dog", :e=>"elephant"}

# Ruby 2.1.0 以上可以用
keys.zip(values).to_h
keys.map(&:to_sym).zip(values).to_h

# Ruby 2.5 以上可以用 transform_keys
Hash[keys.zip(values)].transform_keys { |k| k.to_sym }

⭐️ 搭配 #inject #reduce 使用

Day4 讲过,当我们要转换为比较复杂的资料型态如hash, array的话,比起使用inject, reduce,使用each_with_object会比较简洁。

下列提供inject, each_with_object两种方法比对,虽然Day4已经提过,但还是要强调两者之间的accumulator位置不一样。inject在前面,而each_with_object 在後面

#======= 情境1: 使用inject
[:foo, :bar, :jazz].inject({}) do |hash, item|
  hash[item] = item.to_s.upcase
  hash
end
#=> {:foo=>"FOO", :bar=>"BAR", :jazz=>"JAZZ"}

#======= 情境1: 使用each_with_object
[:foo, :bar, :jazz].each_with_object({}) { |item, hash| hash[item] = item.to_s.upcase }
#=> {:foo=>"FOO", :bar=>"BAR", :jazz=>"JAZZ"}
#======= 情境2: 使用inject
array = [['A', 'a'], ['B', 'b'], ['C', 'c']]
array.inject({}) do |memo, values|
  memo[values.first] = values.last
  memo
end
# => {'A' => 'a', 'B' => 'b', 'C' => 'c'}

#======= 情境2: 使用each_with_object
array = [['A', 'a'], ['B', 'b'], ['C', 'c']]
array.each_with_object({}) do |values, memo|
  memo[values.first] = values.last
end
# => {'A' => 'a', 'B' => 'b', 'C' => 'c'}

Hash to Array

Hash 转为 Array 处理的话相对单纯,Hash只要to_a 就会转为Array

Invoice.statuses.to_a
#=> [["unissued", 0], ["issued", 1], ["allowance", 2], ["allowance_failed", 3]]

#each_with_object

我们也可以使用each_with_object 将原本为Hash的资料转为型态更复杂的Hash

#======= 情境1
t = {0=> "a", 1=> "b", 5=> "c"}
t.each_with_object([]) {|(k,v), a| a[k]=v}
#=> ["a", "b", nil, nil, nil, "c"]


#======= 情境2: 转为array of object
{ foo: 1, bar: 2, jazz: 3 }.each_with_object([]) do |(key, value), array|
  array << { id: value, name: key }
end
# => [{:id=>1, :name=>:foo}, {:id=>2, :name=>:bar}, {:id=>3, :name=>:jazz}]

⭐️ each_with_object 里头只能放阵列与杂凑等可以扩充的容器,若把each_with_object当作inject使用的话,则会出现凄惨情况。

 (1..5).each_with_object(1) { |value, accu| value + accu }  #=> 1 (不是15)

⭐️ 使用rails enum helper,搭配下拉式选单,我们可以用以下方式将Hash 转为阵列

class Order < ApplicationRecord
  # 情境1. module in class
  module Status
    UNPAID      = :unpaid       # 未付款
    PROCESSING  = :processing   # 处理中
    WAITING     = :waiting      # 已出货
    DONE        = :done         # 已完成
    CANCELED    = :canceled     # 已取消
    RETURNED    = :returned     # 退货/退款
  end

  # 情境2. enum
  enum shipping_type: {
    home: 0,  # 宅配
    cvs: 1    # OK/莱尔富/全家
  }
end

通常要使用enum helper转阵列的情境,都是在要将enum转换为前端下拉式选单的资料结构用。

module Admin::OrdersHelper
  #=> [["所有订单状态", nil], ["退货/退款", :returned], ["未付款", :unpaid], 
  #    ["处理中", :processing], ["已完成", :done], ["已出货", :waiting], ["已取消", :canceled]]
  def order_status
    Order::Status.constants
                 .map { |c| Order::Status.const_get(c) }
                 .map { |k| [t("orders.status.#{k}"), k] }
                 .prepend ['所有订单状态', nil]
  end
  
  #=> [['所有配送方式', nil], ["宅配", "home"], ["超商取货", "cvs"]]
  def order_shipping_type(prepend: true)
    options = Order.shipping_types.map { |k, v| [t("orders.shipping_type.#{k}"), k] }
    # radio-button
    return options unless prepend
    # 下拉式选单
    options.prepend ['所有配送方式', nil]
  end
end

结论

今天介绍了Array, Hash之间转换的关系,明天让大家瞧瞧Ruby的时间魔术师。

参考资料


<<:  Day 6:常见的CSS tag+应用

>>:  从 IT 技术面细说 Search Console 的 27 组数字 KPI (6) :网页

Station list screen (1)

最近两篇都是讲 navigation component,入面为了示范设定 navigation 我...

[机派X] Day 8 - 我是 Bash 我调皮,令人匪夷所思的 Bash 语法

引言 昨天介绍了套件管理软件以及图形化使用者界面的安装,也是指令介绍的最後一篇文章。学习了这麽多指令...

(Day3) 执行绪与同步&非同步

单执行绪 JavaScript 是单执行绪的语言,而单执行绪的意思就是一次只做一件事情。 不过这样的...

DAY25-问答页面设计

前言: 几个基本的页面都设计得差不多啦~这次的挑战也快接近尾声了!今天就让阿森来介绍一个有点小特效...

[Day2]-基本的输出入

格式化输出 使用%字元,基本的格式为: Print(“ %输出格式 ” % (变数1,变数2……)...