2016-04-29 50 views
3
創建從現有陣列的新陣列

我有以下2個數組:在Ruby中

fields = ["name", "team", "number", "name", "team", "number", "name", "team", "number"] 
values = ["Patrick Ewing", "New York Knicks", 33, "Rik Smits", "Indiana Pacers", 45, "Bill Russell", "Boston Celtics", 6] 

字段將永遠是唯一的名字,團隊和數量,但值的數量(即,玩家人數)會每次都有所不同。

我想創建的每個字段類型的新陣列,這樣我會得到如下:

names = ["Patrick Ewing", "Rik Smits", "Bill Russell"] 
teams = ["New York Knicks", "Indiana Pacers", "Boston Celtics"] 
numbers = [33,45,6] 

什麼是做到這一點的好辦法?我嘗試了以下方法,但想知道是否還有其他解決方案可以在較大的陣列(最多300個)上執行得更好。或者差異可以忽略不計?

names = values.values_at(*(fields.each_index.select{ |i| fields[i] == "name"})) 
teams = values.values_at(*(fields.each_index.select{ |i| fields[i] == "team"})) 
numbers = values.values_at(*(fields.each_index.select{ |i| fields[i] == "number"})) 
+4

你嘗試過什麼?我們更喜歡這不僅僅是一個代碼寫入服務。這就是說,這看起來像一個'zip'的工作。 –

+2

'fields'列表總是一樣嗎?這是組織數據的一種奇怪方式,因此如果您可以調整它,則可能需要較少的處理。 – tadman

+0

@DaveSchweisguth我嘗試了一些東西並在下面發佈。我明白你爲什麼這麼認爲,但我是一個新手,喜歡通過看看別人會在我自己嘗試一個解決方案時做什麼來學習。 – massaskillz

回答

5

這裏是另一個/簡潔的方式:

> name, team, number = values.each_slice(3).to_a.transpose 
=> name 
> ["Patrick Ewing", "Rik Smits", "Bill Russell"] 
=> team 
> ["New York Knicks", "Indiana Pacers", "Boston Celtics"] 
=> number 
> [33, 45, 6] 

Ruby的Array#transpose的美麗進場時,格外您的問題。

+2

這是一個非常聰明的單線程。尼斯。 – tadman

+2

使用'name,team,number = values.each_slice(3).to_a.transpose'和'name,team,number = values.each_slice(3).each_with_object([]){| _,acc | acc << _} .transpose'? – massaskillz

+0

@massaskillz,因爲兩者都返回相同的數組,所以前者更簡單 - 更可取。 vee,你應該考慮引入massaskillz的建議。 –

1

這裏是一個辦法,按他們的「類型」數組值:

fields = ["name", "team", "number", "name", "team", "number", "name", "team", "number"] 
values = ["Patrick Ewing", "New York Knicks", 33, "Rik Smits", "Indiana Pacers", 45, "Bill Russell", "Boston Celtics", 6] 

field_values = fields.zip(values) 

names = field_values.select { |f, v| f == "name" }.map(&:last) 
# => ["Patrick Ewing", "Rik Smits", "Bill Russell"] 

teams = field_values.select { |f, v| f == "team" }.map(&:last) 
# => ["New York Knicks", "Indiana Pacers", "Boston Celtics"] 

numbers = field_values.select { |f, v| f == "number" }.map(&:last) 
# => [33, 45, 6] 
+1

'select {| f,v | f =='name'}'在這裏可能更簡潔。 – tadman

+0

好點!更新答案以符合您的建議。 – Zoran

2

如果輸入的是一貫以三個一組,你可以使用each_slice

values = ["Patrick Ewing", "New York Knicks", 33, "Rik Smits", "Indiana Pacers", 45, "Bill Russell", "Boston Celtics", 6] 

names = [ ] 
teams = [ ] 
numbers = [ ] 

values.each_slice(3) do |name, team, number| 
    names << name 
    teams << team 
    numbers << number 
end 

這不是組織事物的最佳方式。更多的面向Ruby的風格應該是:

keys = [ :name, :team, :number ] 

players = values.each_slice(3).collect do |set| 
    Hash[keys.zip(set)] 
end 
#=> [{:name=>"Patrick Ewing", :team=>"New York Knicks", :number=>33}, {:name=>"Rik Smits", :team=>"Indiana Pacers", :number=>45}, {:name=>"Bill Russell", :team=>"Boston Celtics", :number=>6}] 

這使您可以輕鬆訪問每條記錄,而無需交叉引用多個數組。即使你在一個表中顯示這一點,需要分別在三件事情,你仍然可以做到這一點,選擇了一個單獨的值:

keys.each do |key| 
    players.each do |player| 
    print '%-20s' % player[key] 
    end 

    puts 
end 

這讓你輸出如下:

Patrick Ewing  Rik Smits   Bill Russell   
New York Knicks  Indiana Pacers  Boston Celtics  
33     45     6     
3

你可以做到這一點的一條線只用group_bywith_index

grouped = values.group_by.with_index {|_, i| fields[i] } 
#=> {"name"=>["Patrick Ewing", "Rik Smits", "Bill Russell"],' 
    "team"=>["New York Knicks", "Indiana Pacers", "Boston Celtics"],    
    "number"=>[33, 45, 6]} 

然後你可以從這個哈希簡單出示你的數組:

name = grouped["name"] 
#=> ["Patrick Ewing", "Rik Smits", "Bill Russell"] 

或者只是:

names, teams, numbers = grouped.values 
+0

非常有創造力! –

1

另一種方法:使用小平面Enumerable#map_by

require 'facets' 
names, teams, numbers = fields.zip(values). 
    map_by { |field, value| [field, value] }. 
    values_at("name", "team", "number")