我有一個哈希陣列,例如:
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"="Brown"}
{"Breed"=>"Pug", "Size"=>"Small", "Colour"=nil}
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"="Brown"}
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=nil}
我想根據給定的搜索條件為他們提供所有 ID 值。例如,按大小搜索應回傳:
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"="Brown", "ID"="0"}
{"Breed"=>"Pug", "Size"=>"Small", "Colour"=nil, "ID"="1"}
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"="Brown", "ID"="0"}
{"Breed"=>"Poodle", "Size"=>"Medium", "Colour"=nil, "ID"="0"}
我撰寫了以下代碼,它使用以下哈希檢查序列中的每個哈希。
for i in 0..data.length-2
data[i].store("ID", i)
for j in i 1..data.length-1
output = (data[i].keys & data[j].keys).select { |k| data[i][k] == data[j][k] }
if output.include? searchTerm
puts "Match!"
puts "---"
data[j].store("ID", data[i]["ID"])
else
puts "No match :("
puts "---"
end
end
puts "---Finished checking row---"
end
puts data
這個問題是雙重的:
A. Nil 值算作匹配,例如在按顏色搜索時:
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"="Brown", "ID"="0"}
{"Breed"=>"Pug", "Size"=>"Small", "Colour"=nil, "ID"="1"}
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"="Brown", "ID"="0"}
{"Breed"=>"Poodle", "Size"=>"Medium", "Colour"=nil, "ID"="1"}
B. 匹配似乎只適用于找到的最后一對,例如在按大小搜索時:
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"="Brown", "ID"="0"}
{"Breed"=>"Pug", "Size"=>"Small", "Colour"=nil, "ID"="1"}
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"="Brown", "ID"="2"}
{"Breed"=>"Poodle", "Size"=>"Medium", "Colour"="White", "ID"="2"}
總之,我想忽略 nil 值,因此它們不會算作匹配項,并且對于同一鍵:值對的所有實體,ID 鍵的值都相同。
uj5u.com熱心網友回復:
不要將 for 回圈用于陣列迭代。Ruby 有很多很好的方法可以做到這一點。
這是我的解決方案:
hashes = [{"Breed" => "Beagle", "Size" => "Medium", "Colour" => "Brown"},
{"Breed" => "Pug", "Size" => "Small", "Colour" => nil},
{"Breed" => "Beagle", "Size" => "Medium", "Colour" => "Brown"},
{"Breed" => "Beagle", "Size" => "Large", "Colour" => nil}]
def search_id(elements, search_key)
# Get rid of elements with nil values.
target_elements = elements.reject {|e| e[search_key].nil?}
resul = []
id = 0
# Iterate through target_elements
target_elements.each do |currentElement|
# Check if exsits an element with the same value in the resul array
match = resul.find {|previousElement| currentElement[search_key] == previousElement[search_key]}
if match
# Use previous id
currentElement[:id] = match[:id]
else
# Assing a new id
currentElement[:id] = id
id = 1
end
# Add element to result
resul << currentElement
end
resul
end
puts search_id(hashes, 'Size')
uj5u.com熱心網友回復:
我認為可能值得考慮首先從搜索的條目中獲取所有唯一值,然后在哈希中為每個條目分配一個 id(以可用術語作為鍵),這樣您就可以簡單地回圈遍歷哈希和從預先生成的哈希中獲取正確的 id。例如:
hashes = [
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>"Brown"},
{"Breed"=>"Pug", "Size"=>"Small", "Colour"=>nil},
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>"Brown"},
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>nil}
]
def append_id(items, search_key)
id_map = items.
map { |hash| hash[search_key] }.
uniq.
reject { |value| value.nil? }.
map.with_index { |value, index| [value, index] }.
to_h
items.each do |hash|
value = hash[search_key]
hash['ID'] = id_map[value] unless value.nil?
end
items
end
append_id(hashes, 'Size')
uj5u.com熱心網友回復:
這是一種方法。
def doit(arr, key)
val_to_id = arr.map { |h| h[key] }
.uniq
.each_with_index.with_object({}) { |(key,i),h| h[key] = i.to_s }
arr.map { |h| h.merge("ID"=>val_to_id[h[key]]) }
end
arr = [{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"="Brown"},
{"Breed"=>"Pug", "Size"=>"Small", "Colour"=nil},
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"="Brown"},
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=nil}]
請參閱Array#map、Array#uniq、Enumerable#each_with_index和Enumerator#with_object。撰寫塊變數 ( |(key,i),h|) 是為了使用陣列分解。
doit(arr,"Breed")
#=> [{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>"Brown", "ID"=>"0"},
# {"Breed"=>"Pug", "Size"=>"Small", "Colour"=>nil, "ID"=>"1"},
# {"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>"Brown", "ID"=>"0"},
# {"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>nil, "ID"=>"0"}]
doit(arr,"Size")
#=> [{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>"Brown", "ID"=>"0"},
# {"Breed"=>"Pug", "Size"=>"Small", "Colour"=>nil, "ID"=>"1"},
# {"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>"Brown", "ID"=>"0"},
# {"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>nil, "ID"=>"0"}]
doit(arr, "Colour")
#=> [{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>"Brown", "ID"=>"0"},
# {"Breed"=>"Pug", "Size"=>"Small", "Colour"=>nil, "ID"=>"1"},
# {"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>"Brown", "ID"=>"0"},
# {"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>nil, "ID"=>"1"}]
步驟如下。
key = "Size"
a = arr.map { |h| h[key] }
#=> ["Medium", "Small", "Medium", "Medium"]
b = a.uniq
#=> ["Medium", "Small"]
val_to_id = b.each_with_index.with_object({}) { |(key,i),h| h[key] = i.to_s }
#=> {"Medium"=>"0", "Small"=>"1"}
arr.map { |h| h.merge("ID"=>val_to_id[h[key]]) }
#=> [{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>"Brown", "ID"=>"0"},
# {"Breed"=>"Pug", "Size"=>"Small", "Colour"=>nil, "ID"=>"1"},
# {"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>"Brown", "ID"=>"0"},
# {"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>nil, "ID"=>"0"}]
讓我們更詳細地考慮最后一個計算。的第一個值arr被傳遞給塊,塊變數h被分配它的值,塊計算被執行,結果回傳給map。
h = arr[0]
#=> {"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>"Brown"}
c = h[key]
#=> "Medium"
d = val_to_id[c]
#=> "Medium"
h.merge("ID"=>d)
#=> {"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>"Brown", "ID"=>"0"}
的其余元素以arr類似方式處理。
uj5u.com熱心網友回復:
前言
正如來自同一位講師的學生對這篇文章和其他相關帖子的評論中提到的那樣,這簡直是一個糟糕的練習。將值附加到 Hash 或 Struct 并不難。您遇到的 X/Y 問題是您試圖模擬連接、索引鍵和中間表等資料庫的內置功能。重新發明輪子通常是個壞主意。
也就是說,如果您可以對您的資料做出一些基本保證,例如 :breed 將始終有一個值,并且您實際上可以合并來自不同鍵的值而不會破壞重要資料或使空值的整個概念無效資料庫欄位,那么你可以非常接近你想要的。仍然存在很多邊緣情況,因為資料是特發性的,未標準化,并且為相似但不同的記錄提供相同 :id 的概念(無論您是否已合并它們)似乎是有問題的。
盡管有所有這些警告,但這里有一個可行的解決方案,它應該解決您的實際問題和潛在問題,以及您的一位同事發布的相關問題。我不打算解釋每一行代碼,但我相當徹底地記錄了它,以便您可以嘗試它。
作業犬“資料庫”
申請代碼
#!/usr/bin/env ruby
require 'logger'
Dog = Struct.new :breed, :size, :color, :id
class ReinventingTheDatabase
# Here's a semi-randomized "database" of sample data, which is exercised by the self-tests at the bottom of the code.
#
# @note No attempt is made to handle the use case of a missing breed, which is used elsewhere to sort and merge in a
# sensible way.
# @return [Array<Struct>] populated by Dog objects for use in data lookups and merging
DOG_DATABASE = [
['terrier', 'small', 'brown'],
['beagle', 'medium', 'brown'],
['pug', 'small', nil],
['beagle', nil, 'brown'],
['beagle', 'medium', 'brown'],
['beagle', 'medium', nil],
['poodle', 'medium', nil],
['schnauzer', 'small', 'grey'],
['poodle', nil, 'white'],
['schnauzer', 'small', nil],
].map { Dog.new *_1 }.freeze
# @attr_reader dogs [Array<Array>] carries a copy of the DOG_DATABASE constant converted to a Dog struct for data lookups
# @attr_reader last_query [Hash] holds the current database query for the instance
# @attr_reader last_query_result [Array<Struct>] the complete results of the data lookup
# @attr_reader last_merge [Array<Struct>] routinely updated as the @last_query_result is merged and manipulated
attr_reader *%i[dogs last_query last_query_result last_merge logger]
# @note You can get debugging output by adding LOG_LEVEL='debug' to your environment, or tweak ENV in your REPL.
# @param database [Array<Struct>, #members] values to populate your pseudo-database with; must
# :respond_to?(:members) or stuff will break
# @param logfile [String, StringIO] destination compatible with Ruby's standard library Logger
# @return [void] instantiate your faux database class
def initialize database=DOG_DATABASE, logfile: $stderr
@dogs = database.dup
@logger = Logger.new logfile, level: ENV.fetch('LOG_LEVEL', :info)
end
# Public interface for the class.
#
# @param kwargs [Hash, Array<Hash>] one or more keyword arguments that are members of members of Dog < Struct class
# to use as search terms
# @see Dog
# @return [Array<Struct>] merged and tagged results of the data lookup and munging
def query **kwargs
@last_query = kwargs
@logger.debug "Querying database for: #{@last_query}"
query_result = db_query
@logger.debug "Final result after merging results: #{@last_merge}"
printf "Query: %{kwargs}\nResult: %{result}\n\n", {kwargs: kwargs, result: db_query.map(&:to_h)} unless
@logger.debug?
query_result
end
private
# @return [Array<Struct>] matching elements that have been merged and tagged with an :id attribute
def db_query
rows = []
@dogs.map { |row| @last_query.map { rows.push(row) if row[_1] == _2 } }
@last_merge = (@last_query_result = rows).dup
merge_struct_members
insert_ids_into_last_merge
end
# @return [Array<Struct>] @last_merge including :id attributes
def insert_ids_into_last_merge
idx = -1
@last_merge.uniq!
@last_merge.map! { _1.id = idx =1; _1 }
end
# Merge Struct members that are nil when possible.
#
# @param m [Symbol] Dog#member
# @return [Array<Struct>] modified @last_query_result
def merge_struct_members
Dog.members[0..-2].map do |m|
@last_merge.uniq.sort_by { _1.breed }.each_cons(2).map do |row1, row2|
cons_rows = [row1[m], row2[m]].compact
row1[m], row2[m] = cons_rows * 2 if cons_rows.one? && row1.breed == row2.breed
end
end
end
end
if __FILE__ == $0
db = ReinventingTheDatabase.new
db.query size: 'small'
db.query size: 'medium'
db.query size: 'small', color: 'brown'
db.query breed: 'poodle'
end
# vim: ft=ruby et sw=2 tw=120
控制臺輸出
ruby dogs.rb當使用(或者如果它是可執行的)從 shell 運行時,./dogs.rb您應該得到以下輸出:
Query: {:size=>"small"}
Result: [{:breed=>"terrier", :size=>"small", :color=>"brown", :id=>0}, {:breed=>"pug", :size=>"small", :color=>nil, :id=>1}, {:breed=>"schnauzer", :size=>"small", :color=>"grey", :id=>2}, {:breed=>"schnauzer", :size=>"small", :color=>"grey", :id=>3}]
Query: {:size=>"medium"}
Result: [{:breed=>"beagle", :size=>"medium", :color=>"brown", :id=>0}, {:breed=>"beagle", :size=>"medium", :color=>"brown", :id=>1}, {:breed=>"poodle", :size=>"medium", :color=>nil, :id=>2}]
Query: {:size=>"small", :color=>"brown"}
Result: [{:breed=>"terrier", :size=>"small", :color=>"brown", :id=>0}, {:breed=>"beagle", :size=>"medium", :color=>"brown", :id=>1}, {:breed=>"pug", :size=>"small", :color=>nil, :id=>2}, {:breed=>"beagle", :size=>"medium", :color=>"brown", :id=>3}, {:breed=>"beagle", :size=>"medium", :color=>"brown", :id=>4}, {:breed=>"beagle", :size=>"medium", :color=>"brown", :id=>5}, {:breed=>"schnauzer", :size=>"small", :color=>"grey", :id=>6}, {:breed=>"schnauzer", :size=>"small", :color=>"grey", :id=>7}]
Query: {:breed=>"poodle"}
Result: [{:breed=>"poodle", :size=>"medium", :color=>"white", :id=>0}, {:breed=>"poodle", :size=>"medium", :color=>"white", :id=>1}]
如果您在啟用除錯日志記錄的情況下運行它,您將獲得更多可見性。例如,當LOG_LEVEL='debug' ruby dogs.rb你應該看到呼叫時:
D, [2022-05-29T23:24:09.055928 #43664] DEBUG -- : Querying database for: {:size=>"small"}
D, [2022-05-29T23:24:09.056087 #43664] DEBUG -- : Final result after merging results: [#<struct Dog breed="terrier", size="small", color="brown", id=0>, #<struct Dog breed="pug", size="small", color=nil, id=1>, #<struct Dog breed="schnauzer", size="small", color="grey", id=2>]
D, [2022-05-29T23:24:09.056120 #43664] DEBUG -- : Querying database for: {:size=>"medium"}
D, [2022-05-29T23:24:09.056178 #43664] DEBUG -- : Final result after merging results: [#<struct Dog breed="beagle", size="medium", color="brown", id=0>, #<struct Dog breed="poodle", size="medium", color=nil, id=1>]
D, [2022-05-29T23:24:09.056204 #43664] DEBUG -- : Querying database for: {:size=>"small", :color=>"brown"}
D, [2022-05-29T23:24:09.056285 #43664] DEBUG -- : Final result after merging results: [#<struct Dog breed="terrier", size="small", color="brown", id=0>, #<struct Dog breed="beagle", size="medium", color="brown", id=1>, #<struct Dog breed="pug", size="small", color=nil, id=2>, #<struct Dog breed="beagle", size="medium", color="brown", id=3>, #<struct Dog breed="schnauzer", size="small", color="grey", id=4>, #<struct Dog breed="schnauzer", size="small", color="grey", id=5>]
D, [2022-05-29T23:24:09.056311 #43664] DEBUG -- : Querying database for: {:breed=>"poodle"}
D, [2022-05-29T23:24:09.056357 #43664] DEBUG -- : Final result after merging results: [#<struct Dog breed="poodle", size="medium", color="white", id=0>, #<struct Dog breed="poodle", size="medium", color="white", id=1>]
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/484995.html
