根据出现时间整理唯一元素的值 Ruby
Organize values of unique elements based on occurrence time Ruby
请帮忙解决这个问题。我有下面的 2 个数组。数组 a 包含小时数,数组 b 包含相同的小时数,然后是在这些小时发生的值。
a = ["1015","1240","1732"]
b = ["1015","X|2","D|5","1240","B|11","F|8","X|7","1732","D|9","X|1","B|3"]
所以在数组 b 中:
元素 "X|2","D|5"
发生在 10:15
小时
元素 "B|11","F|8","X|7"
发生在 12:40
小时
元素 "D|9","X|1","B|3"
发生在 17:32
小时
B 中每个元素的第一部分可以重复,例如,X 发生在 3 小时内,具有不同的值,因此在输出中,我想打印小时数和唯一值,
这是 X、D、B 和 F
我正在寻找的输出是:
HOUR X D B F
1015 2 5
1240 7 11 8
1732 1 9 3
我目前的代码如下,但我仍然无法按所需顺序组织输出。
val=[]
headers=[]
b.each{|v|
if v.include? "|"
headers << v.split("|")[0]
val << v.split("|")[1]
else
val << ["HOUR",v]
end
}
puts ["HOURS",headers.uniq].join(" ")
puts val
我的代码的当前输出:
HOURS X D B F
HOUR
1015
2
5
HOUR
1240
11
8
7
HOUR
1732
9
1
3
我不太确定您是否需要 a
数组,因为它的所有值也出现在 b
数组中 - 我将它从我的代码中删除。
它在第一步中做了什么:它将原始数组缩减为一个新数组,它将小时和其中发生的事情组合成子数组。我通过检查当前值是否为数字来执行此操作,如果不是,我将一个新操作写入当前时间 - 同时,我以正确的顺序跟踪所有可能的列。
columns = ["HOUR"]
merged = b.reduce([]) do |accumulator, value|
if value =~ /\A[-+]?[0-9]*\.?[0-9]+\Z/
accumulator.push({"HOUR" => value})
else
parts = value.split('|')
columns.push(parts[0]) unless columns.include?(parts[0])
accumulator[-1][parts[0]] = parts[1]
end
accumulator
end
merged
现在是 [{"HOUR"=>"1015", "X"=>"2", "D"=>"5"}, {"HOUR"=>"1240", "B"=>"11", "F"=>"8", "X"=>"7"}, {"HOUR"=>"1732", "D"=>"9", "X"=>"1", "B"=>"3"}]
- 而 columns
现在是 ["HOUR", "X", "D", "B", "F"]
从那里开始,我们可以准备类似 csv 的数据:
csv_like = [columns] + merged.map { |dataset| columns.map { |column| dataset.fetch(column, nil) } }
csv_like
现在是 [["HOUR", "X", "D", "B", "F"], ["1015", "2", "5", nil, nil], ["1240", "7", nil, "11", "8"], ["1732", "1", "9", "3", nil]]
这应该是您要搜索的内容 - 您现在可以使用此数据轻松创建 CSV 或 HTML table。
我假设 a
仅包含 b
中的时间,已排序。由于可以计算,因此无需提供该信息作为输入。
代码
def print_table(data, time_label, column_spacing)
h = data.slice_before { |s| !s.include?('|') }.
each_with_object({}) { |(t,*a),h|
h[t] = a.map { |s| s.split('|') }.to_h.tap { |g| g.default = '' } }
row_labels = h.keys.sort
column_labels = h.values_at(*row_labels).reduce([]) { |a,g| a | g.keys }
image = [[time_label, *column_labels],
*row_labels.map { |time| [time, *h[time].values_at(*column_labels)] }]
row_label_width, *column_widths = image.transpose.map { |r| r.map(&:size).max }
print_image(image, row_label_width, column_widths, column_spacing)
end
def print_image(image, row_label_width, column_widths, column_spacing)
image.each do |time, *values|
print time.ljust(row_label_width)
values.zip(column_widths).each { |s,width| print s.rjust(width + column_spacing) }
puts
end
end
例子
b = ["1240", "B|11", "F|8", "X|7",
"1015", "X|2", "D|5",
"1732", "D|9", "X|1", "B|3"]
time_label = "HOUR"
column_spacing = 2
print_table(b, time_label, column_spacing)
打印
HOUR X D B F
1015 2 5
1240 7 11 8
1732 1 9 3
请注意 b
中的时间未按排序顺序排列。
说明
对于 Example 部分中的值,第一步是将数组 b
的元素按时间分组(数组) .
groups = b.slice_before { |s| !s.include?('|') }
#=> #<Enumerator: #<Enumerator::Generator:0x000000022b2490>:each>
参见 Enumerable#slice_before。我们可以通过将其转换为数组来查看此枚举器将生成的对象。
groups.to_a
#=> [["1240", "B|11", "F|8", "X|7"],
# ["1015", "X|2", "D|5"],
# ["1732", "D|9", "X|1", "B|3"]]
接下来,让我们将 groups
转换为散列。
h = groups.each_with_object({}) { |(t,*a),h|
h[t] = a.map { |s| s.split('|') }.
to_h.
tap { |g| g.default = '' } }
#=> {"1240"=>{"B"=>"11", "F"=>"8", "X"=>"7"},
# "1015"=>{"X"=>"2", "D"=>"5"},
# "1732"=>{"D"=>"9", "X"=>"1", "B"=>"3"}}
参见 Enumerable#each_with_object, Array#to_h, Object#tap and Hash#default=。 g.default = ''
为散列分配一个空的默认值 space。这意味着如果 g
没有键 k
,g[k]
returns 一个空的 space。例如,h["1015"]["B"] #=> ""
。 g.default = ''
returns ''
,这就是为什么它被包含在 tap
块中的原因,其中 returns g
具有默认定义。
This article 提供了 splat 运算符的用法说明。 (在这里,简而言之:[1, *[2, 3]] #=> [1, 2, 3]
)。
对于列标签,我们有几个选项。无论如何,我们首先需要 h
的值(散列)中的唯一键对应于 row_labels
.
中的键
row_labels = h.keys.sort
#=> ["1015", "1240", "1732"]
column_labels = h.values_at(*row_labels)
#=> [{"X"=>"2", "D"=>"5"},
# {"B"=>"11", "F"=>"8", "X"=>"7"},
# {"D"=>"9", "X"=>"1", "B"=>"3"}]
column_labels = column_labels.reduce([]) { |a,g| a | g.keys }
#=> ["X", "D", "B", "F"]
参见 Enumerable#values_at, Enumerable#reduce (aka inject
) and Array#|。我假设这给出了所需的列顺序,但是 column_labels
的元素可以根据需要重新排序。我在答案的最后部分提出了两个可能的选择。
我们接下来构建一个数组,其中包含要打印的 table 中的所有值。
image = [[time_label, *column_labels],
*row_labels.map { |time| [time, *h[time].values_at(*column_labels)] }]
#=> [["HOUR", "X", "D", "B", "F"],
# ["1015", "2", "5", "", ""],
# ["1240", "7", "", "11", "8"],
# ["1732", "1", "9", "3", ""]]
Enumerable#values_at
提取 h[time]
中与 table 的每一行对应的值(散列),并按所需顺序排列。
然后我们可以打印 table 如下。
row_label_width, *column_widths = image.transpose.map { |r| r.map(&:size).max }
# => [4, 1, 1, 2, 1]
所以
row_label_width
#=> 4
column_widths
#=> [1, 1, 2, 1]
image.each do |time, *values|
print time.ljust(row_label_width)
values.zip(column_widths).each { |s,width| print s.rjust(width + column_spacing) }
puts
end
打印 示例 部分中显示的 table。
列顺序
正如我之前所说,column_labels
的元素可以根据需要重新排序。一种可能性是按字母顺序对标签进行排序。
column_labels = h.values_at(*row_labels).reduce([]) { |a,g| a | g.keys }.sort!
#=> ["B", "D", "F", "X"]
另一个是我们得到了所有可能的列标签的所需顺序。
desired = ["Y", "F", "D", "Z", "B", "X"]
然后计算以下内容。
column_labels = desired & h.values_at(*row_labels).reduce([]) { |a,g| a | g.keys }
#=> ["F", "D", "B", "X"]
请帮忙解决这个问题。我有下面的 2 个数组。数组 a 包含小时数,数组 b 包含相同的小时数,然后是在这些小时发生的值。
a = ["1015","1240","1732"]
b = ["1015","X|2","D|5","1240","B|11","F|8","X|7","1732","D|9","X|1","B|3"]
所以在数组 b 中:
元素 "X|2","D|5"
发生在 10:15
元素 "B|11","F|8","X|7"
发生在 12:40
元素 "D|9","X|1","B|3"
发生在 17:32
B 中每个元素的第一部分可以重复,例如,X 发生在 3 小时内,具有不同的值,因此在输出中,我想打印小时数和唯一值, 这是 X、D、B 和 F
我正在寻找的输出是:
HOUR X D B F
1015 2 5
1240 7 11 8
1732 1 9 3
我目前的代码如下,但我仍然无法按所需顺序组织输出。
val=[]
headers=[]
b.each{|v|
if v.include? "|"
headers << v.split("|")[0]
val << v.split("|")[1]
else
val << ["HOUR",v]
end
}
puts ["HOURS",headers.uniq].join(" ")
puts val
我的代码的当前输出:
HOURS X D B F
HOUR
1015
2
5
HOUR
1240
11
8
7
HOUR
1732
9
1
3
我不太确定您是否需要 a
数组,因为它的所有值也出现在 b
数组中 - 我将它从我的代码中删除。
它在第一步中做了什么:它将原始数组缩减为一个新数组,它将小时和其中发生的事情组合成子数组。我通过检查当前值是否为数字来执行此操作,如果不是,我将一个新操作写入当前时间 - 同时,我以正确的顺序跟踪所有可能的列。
columns = ["HOUR"]
merged = b.reduce([]) do |accumulator, value|
if value =~ /\A[-+]?[0-9]*\.?[0-9]+\Z/
accumulator.push({"HOUR" => value})
else
parts = value.split('|')
columns.push(parts[0]) unless columns.include?(parts[0])
accumulator[-1][parts[0]] = parts[1]
end
accumulator
end
merged
现在是 [{"HOUR"=>"1015", "X"=>"2", "D"=>"5"}, {"HOUR"=>"1240", "B"=>"11", "F"=>"8", "X"=>"7"}, {"HOUR"=>"1732", "D"=>"9", "X"=>"1", "B"=>"3"}]
- 而 columns
现在是 ["HOUR", "X", "D", "B", "F"]
从那里开始,我们可以准备类似 csv 的数据:
csv_like = [columns] + merged.map { |dataset| columns.map { |column| dataset.fetch(column, nil) } }
csv_like
现在是 [["HOUR", "X", "D", "B", "F"], ["1015", "2", "5", nil, nil], ["1240", "7", nil, "11", "8"], ["1732", "1", "9", "3", nil]]
这应该是您要搜索的内容 - 您现在可以使用此数据轻松创建 CSV 或 HTML table。
我假设 a
仅包含 b
中的时间,已排序。由于可以计算,因此无需提供该信息作为输入。
代码
def print_table(data, time_label, column_spacing)
h = data.slice_before { |s| !s.include?('|') }.
each_with_object({}) { |(t,*a),h|
h[t] = a.map { |s| s.split('|') }.to_h.tap { |g| g.default = '' } }
row_labels = h.keys.sort
column_labels = h.values_at(*row_labels).reduce([]) { |a,g| a | g.keys }
image = [[time_label, *column_labels],
*row_labels.map { |time| [time, *h[time].values_at(*column_labels)] }]
row_label_width, *column_widths = image.transpose.map { |r| r.map(&:size).max }
print_image(image, row_label_width, column_widths, column_spacing)
end
def print_image(image, row_label_width, column_widths, column_spacing)
image.each do |time, *values|
print time.ljust(row_label_width)
values.zip(column_widths).each { |s,width| print s.rjust(width + column_spacing) }
puts
end
end
例子
b = ["1240", "B|11", "F|8", "X|7",
"1015", "X|2", "D|5",
"1732", "D|9", "X|1", "B|3"]
time_label = "HOUR"
column_spacing = 2
print_table(b, time_label, column_spacing)
打印
HOUR X D B F
1015 2 5
1240 7 11 8
1732 1 9 3
请注意 b
中的时间未按排序顺序排列。
说明
对于 Example 部分中的值,第一步是将数组 b
的元素按时间分组(数组) .
groups = b.slice_before { |s| !s.include?('|') }
#=> #<Enumerator: #<Enumerator::Generator:0x000000022b2490>:each>
参见 Enumerable#slice_before。我们可以通过将其转换为数组来查看此枚举器将生成的对象。
groups.to_a
#=> [["1240", "B|11", "F|8", "X|7"],
# ["1015", "X|2", "D|5"],
# ["1732", "D|9", "X|1", "B|3"]]
接下来,让我们将 groups
转换为散列。
h = groups.each_with_object({}) { |(t,*a),h|
h[t] = a.map { |s| s.split('|') }.
to_h.
tap { |g| g.default = '' } }
#=> {"1240"=>{"B"=>"11", "F"=>"8", "X"=>"7"},
# "1015"=>{"X"=>"2", "D"=>"5"},
# "1732"=>{"D"=>"9", "X"=>"1", "B"=>"3"}}
参见 Enumerable#each_with_object, Array#to_h, Object#tap and Hash#default=。 g.default = ''
为散列分配一个空的默认值 space。这意味着如果 g
没有键 k
,g[k]
returns 一个空的 space。例如,h["1015"]["B"] #=> ""
。 g.default = ''
returns ''
,这就是为什么它被包含在 tap
块中的原因,其中 returns g
具有默认定义。
This article 提供了 splat 运算符的用法说明。 (在这里,简而言之:[1, *[2, 3]] #=> [1, 2, 3]
)。
对于列标签,我们有几个选项。无论如何,我们首先需要 h
的值(散列)中的唯一键对应于 row_labels
.
row_labels = h.keys.sort
#=> ["1015", "1240", "1732"]
column_labels = h.values_at(*row_labels)
#=> [{"X"=>"2", "D"=>"5"},
# {"B"=>"11", "F"=>"8", "X"=>"7"},
# {"D"=>"9", "X"=>"1", "B"=>"3"}]
column_labels = column_labels.reduce([]) { |a,g| a | g.keys }
#=> ["X", "D", "B", "F"]
参见 Enumerable#values_at, Enumerable#reduce (aka inject
) and Array#|。我假设这给出了所需的列顺序,但是 column_labels
的元素可以根据需要重新排序。我在答案的最后部分提出了两个可能的选择。
我们接下来构建一个数组,其中包含要打印的 table 中的所有值。
image = [[time_label, *column_labels],
*row_labels.map { |time| [time, *h[time].values_at(*column_labels)] }]
#=> [["HOUR", "X", "D", "B", "F"],
# ["1015", "2", "5", "", ""],
# ["1240", "7", "", "11", "8"],
# ["1732", "1", "9", "3", ""]]
Enumerable#values_at
提取 h[time]
中与 table 的每一行对应的值(散列),并按所需顺序排列。
然后我们可以打印 table 如下。
row_label_width, *column_widths = image.transpose.map { |r| r.map(&:size).max }
# => [4, 1, 1, 2, 1]
所以
row_label_width
#=> 4
column_widths
#=> [1, 1, 2, 1]
image.each do |time, *values|
print time.ljust(row_label_width)
values.zip(column_widths).each { |s,width| print s.rjust(width + column_spacing) }
puts
end
打印 示例 部分中显示的 table。
列顺序
正如我之前所说,column_labels
的元素可以根据需要重新排序。一种可能性是按字母顺序对标签进行排序。
column_labels = h.values_at(*row_labels).reduce([]) { |a,g| a | g.keys }.sort!
#=> ["B", "D", "F", "X"]
另一个是我们得到了所有可能的列标签的所需顺序。
desired = ["Y", "F", "D", "Z", "B", "X"]
然后计算以下内容。
column_labels = desired & h.values_at(*row_labels).reduce([]) { |a,g| a | g.keys }
#=> ["F", "D", "B", "X"]