This is a simple code to parse a simple string in ruby
这是一个解析ruby中的简单字符串的简单代码
str = "Amdh#34HB!x"
length = str.length
upper = str.scan(/[A-Z]/).count #count upper chars
lower = str.scan(/[a-z]/).count #count lower chars
digit = str.scan(/[0-9]/).count #count digits
special = str.scan(/[^a-z0-9]/i).count #count special chars
result = "Length : " + length.to_s + " Upper : " + upper.to_s + " Lower : " + lower.to_s + " Digit : " + digit .to_s + " Special : " + special.to_s
The result is given as "Length : 11 Upper : 3 Lower : 4 Digit : 2 Special : 2"
结果为“长度:11上:3下:4位数:2特殊:2”
I want to do the same things to an array of strings
我想对一组字符串做同样的事情
array = ["Amdh#34HB!x", "AzErtY45", "#1A3bhk2"]
and so I can know the details like above of each element of the array
所以我可以知道数组中每个元素的详细信息
Example :
示例:
array[0] = "Length : 11 Upper : 3 Lower : 4 Digit : 2 Special : 2"
array[1] = "Length : 8 Upper : 3 Lower : 3 Digit : 2 Special : 0"
array[2] = "Length : 8 Upper : 1 Lower : 3 Digit : 3 Special : 1"
....
....
I know the answer seems simple by using each
method but didn't find the right way to do it.
我知道通过使用每种方法答案似乎很简单,但没有找到正确的方法。
The code above is not optimised, if there is any better suggestion, you are welcome!
上面的代码没有优化,如果有更好的建议,欢迎您!
6 个解决方案
#1
2
You need only make a single pass through the string to obtain the needed counts.
您只需要通过字符串进行一次传递即可获得所需的计数。
def obtain_counts(str)
str.each_char.with_object(Hash.new(0)) do |c,h|
h[case(c)
when /[[:upper:]]/ then :upper
when /[[:lower:]]/ then :lower
when /[[:digit:]]/ then :digit
else :special
end] += 1
end
end
def construct_array(arr)
arr.map! { |str|
"Length : #{str.length} Upper : %d Lower : %d Digit : %d Special : %d" %
obtain_counts(str).values_at(:upper, :lower, :digit, :special) }
end
array = ["Amdh#34HB!x", "AzErtY45", "#1A3bhk2"]
construct_array(array)
#=> ["Length : 11 Upper : 3 Lower : 4 Digit : 2 Special : 2",
# "Length : 8 Upper : 3 Lower : 3 Digit : 2 Special : 0",
# "Length : 8 Upper : 1 Lower : 3 Digit : 3 Special : 1"]
array
#=> ["Length : 11 Upper : 3 Lower : 4 Digit : 2 Special : 2",
# "Length : 8 Upper : 3 Lower : 3 Digit : 2 Special : 0",
# "Length : 8 Upper : 1 Lower : 3 Digit : 3 Special : 1"]
Note that
注意
["Amdh#34HB!x", "AzErtY45", "#1A3bhk2"].map { |str| obtain_counts(str) }
#=> [{:upper=>3, :lower=>4, :special=>2, :digit=>2},
# {:upper=>3, :lower=>3, :digit=>2},
# {:special=>1, :digit=>3, :upper=>1, :lower=>3}]
Notice that the second hash in this array has no key :special
(because the second string contained no special characters). That explains why, in obtain_counts
, we need Hash.new(0)
(empty hash with default 0
), rather than simply {}
.
请注意,此数组中的第二个哈希没有键:special(因为第二个字符串不包含特殊字符)。这就解释了为什么在obtain_counts中,我们需要Hash.new(0)(默认为0的空哈希),而不是简单的{}。
#2
2
You may use simple map
to do this:
您可以使用简单的地图来执行此操作:
array = ["Amdh#34HB!x", "AzErtY45", "#1A3bhk2"]
result = array.map do |str|
length = str.length
upper = str.scan(/[A-Z]/).count #count upper chars
lower = str.scan(/[a-z]/).count #count lower chars
digit = str.scan(/[0-9]/).count #count digits
special = str.scan(/[^a-z0-9]/i).count #count special chars
{length: length, upper: upper, lower: lower, digit: digit, special: special}
end
[117] pry(main)> result
=> [{:length=>11, :upper=>3, :lower=>4, :digit=>2, :special=>2},
{:length=>8, :upper=>3, :lower=>3, :digit=>2, :special=>0},
{:length=>8, :upper=>1, :lower=>3, :digit=>3, :special=>1}]
#3
1
I guess you want to do this:
我想你想这样做:
array = ["Amdh#34HB!x", "AzErtY45", "#1A3bhk2"]
result = []
array.each do |str|
length = str.length
upper = str.scan(/[A-Z]/).count #count upper chars
lower = str.scan(/[a-z]/).count #count lower chars
digit = str.scan(/[0-9]/).count #count digits
special = str.scan(/[^a-z0-9]/i).count #count special chars
result << "Length : " + length.to_s + " Upper : " + upper.to_s + " Lower : " + lower.to_s + " Digit : " + digit .to_s + " Special : " + special.to_s
end
puts result
#4
1
try this solution:
尝试此解决方案:
def string_check(str)
result = str.scan(/([A-Z])|([a-z])|([0-9])|([^a-z0-9])/).inject([0,0,0,0]) do
|sum,arr| sum.map.with_index{|e,i| e+(arr[i].nil? ? 0: 1) if !arr.nil?}
end
"Length : #{str.size} Upper : #{result[0]} Lower : #{result[1]} Digit : #{result[2]} Special : #{result[3]}"
end
array = ["Amdh#34HB!x", "AzErtY45", "#1A3bhk2"]
array.each {|s| puts string_check(s)}
outputs:
输出:
Length : 11 Upper : 3 Lower : 4 Digit : 2 Special : 2
Length : 8 Upper : 3 Lower : 3 Digit : 2 Special : 0
Length : 8 Upper : 1 Lower : 3 Digit : 3 Special : 1
#5
1
More DRY solution:
更多DRY解决方案:
array = ["Amdh#34HB!x", "AzErtY45", "#1A3bhk2"]
formats = { Upper: /[A-Z]/,
Lower: /[a-z]/,
Digit: /[0-9]/,
Special: /[^a-z0-9]/i }
array.map do |e|
"Length: #{e.length}, " +
formats.map {|k, v| "#{k}: #{e.scan(v).count}" }.join(', ')
end
#=> ["Length: 11, Upper: 3, Lower: 4, Digit: 2, Special: 2",
# "Length: 8, Upper: 3, Lower: 3, Digit: 2, Special: 0",
# "Length: 8, Upper: 1, Lower: 3, Digit: 3, Special: 1"]
#6
1
Here's a start to help you move into a more OO Ruby script:
这是一个开始帮助您进入更多OO Ruby脚本:
class Foo
attr_reader :str, :str_len, :upper, :lower, :digit, :punctuation
def initialize(str)
@str = str
@str_len = str.length
@upper, @lower, @digit, @punctuation = %w[upper lower digit punct].map { |re| str.scan(/[[:#{re}:]]/).count }
end
def to_s
('"%s": ' % str) +
[:str_len, :upper, :lower, :digit, :punctuation].map { |s|
'%s: %s' % [s.to_s.upcase, instance_variable_get("@#{s}")]
}.join(' ')
end
end
array = ["Amdh#34HB!x", "AzErtY45", "#1A3bhk2"].map { |s| Foo.new(s) }
puts array.map(&:to_s)
Which, when run, outputs:
运行时输出:
"Amdh#34HB!x": STR_LEN: 11 UPPER: 3 LOWER: 4 DIGIT: 2 PUNCTUATION: 2
"AzErtY45": STR_LEN: 8 UPPER: 3 LOWER: 3 DIGIT: 2 PUNCTUATION: 0
"#1A3bhk2": STR_LEN: 8 UPPER: 1 LOWER: 3 DIGIT: 3 PUNCTUATION: 1
The regular expression classes like [[:upper:]]
are POSIX definitions, which help relieve some of the visual noise of a traditional expression's classes. See the Regexp documentation for more information.
像[[:upper:]]这样的正则表达式类是POSIX定义,它有助于减轻传统表达式类的一些视觉噪声。有关更多信息,请参阅Regexp文档。
It can be DRYer but that's an exercise for the student. You should be able to coerce this into something closer to what you want.
它可以是DRYer,但这是学生的练习。你应该能够将它强制为更接近你想要的东西。
#1
2
You need only make a single pass through the string to obtain the needed counts.
您只需要通过字符串进行一次传递即可获得所需的计数。
def obtain_counts(str)
str.each_char.with_object(Hash.new(0)) do |c,h|
h[case(c)
when /[[:upper:]]/ then :upper
when /[[:lower:]]/ then :lower
when /[[:digit:]]/ then :digit
else :special
end] += 1
end
end
def construct_array(arr)
arr.map! { |str|
"Length : #{str.length} Upper : %d Lower : %d Digit : %d Special : %d" %
obtain_counts(str).values_at(:upper, :lower, :digit, :special) }
end
array = ["Amdh#34HB!x", "AzErtY45", "#1A3bhk2"]
construct_array(array)
#=> ["Length : 11 Upper : 3 Lower : 4 Digit : 2 Special : 2",
# "Length : 8 Upper : 3 Lower : 3 Digit : 2 Special : 0",
# "Length : 8 Upper : 1 Lower : 3 Digit : 3 Special : 1"]
array
#=> ["Length : 11 Upper : 3 Lower : 4 Digit : 2 Special : 2",
# "Length : 8 Upper : 3 Lower : 3 Digit : 2 Special : 0",
# "Length : 8 Upper : 1 Lower : 3 Digit : 3 Special : 1"]
Note that
注意
["Amdh#34HB!x", "AzErtY45", "#1A3bhk2"].map { |str| obtain_counts(str) }
#=> [{:upper=>3, :lower=>4, :special=>2, :digit=>2},
# {:upper=>3, :lower=>3, :digit=>2},
# {:special=>1, :digit=>3, :upper=>1, :lower=>3}]
Notice that the second hash in this array has no key :special
(because the second string contained no special characters). That explains why, in obtain_counts
, we need Hash.new(0)
(empty hash with default 0
), rather than simply {}
.
请注意,此数组中的第二个哈希没有键:special(因为第二个字符串不包含特殊字符)。这就解释了为什么在obtain_counts中,我们需要Hash.new(0)(默认为0的空哈希),而不是简单的{}。
#2
2
You may use simple map
to do this:
您可以使用简单的地图来执行此操作:
array = ["Amdh#34HB!x", "AzErtY45", "#1A3bhk2"]
result = array.map do |str|
length = str.length
upper = str.scan(/[A-Z]/).count #count upper chars
lower = str.scan(/[a-z]/).count #count lower chars
digit = str.scan(/[0-9]/).count #count digits
special = str.scan(/[^a-z0-9]/i).count #count special chars
{length: length, upper: upper, lower: lower, digit: digit, special: special}
end
[117] pry(main)> result
=> [{:length=>11, :upper=>3, :lower=>4, :digit=>2, :special=>2},
{:length=>8, :upper=>3, :lower=>3, :digit=>2, :special=>0},
{:length=>8, :upper=>1, :lower=>3, :digit=>3, :special=>1}]
#3
1
I guess you want to do this:
我想你想这样做:
array = ["Amdh#34HB!x", "AzErtY45", "#1A3bhk2"]
result = []
array.each do |str|
length = str.length
upper = str.scan(/[A-Z]/).count #count upper chars
lower = str.scan(/[a-z]/).count #count lower chars
digit = str.scan(/[0-9]/).count #count digits
special = str.scan(/[^a-z0-9]/i).count #count special chars
result << "Length : " + length.to_s + " Upper : " + upper.to_s + " Lower : " + lower.to_s + " Digit : " + digit .to_s + " Special : " + special.to_s
end
puts result
#4
1
try this solution:
尝试此解决方案:
def string_check(str)
result = str.scan(/([A-Z])|([a-z])|([0-9])|([^a-z0-9])/).inject([0,0,0,0]) do
|sum,arr| sum.map.with_index{|e,i| e+(arr[i].nil? ? 0: 1) if !arr.nil?}
end
"Length : #{str.size} Upper : #{result[0]} Lower : #{result[1]} Digit : #{result[2]} Special : #{result[3]}"
end
array = ["Amdh#34HB!x", "AzErtY45", "#1A3bhk2"]
array.each {|s| puts string_check(s)}
outputs:
输出:
Length : 11 Upper : 3 Lower : 4 Digit : 2 Special : 2
Length : 8 Upper : 3 Lower : 3 Digit : 2 Special : 0
Length : 8 Upper : 1 Lower : 3 Digit : 3 Special : 1
#5
1
More DRY solution:
更多DRY解决方案:
array = ["Amdh#34HB!x", "AzErtY45", "#1A3bhk2"]
formats = { Upper: /[A-Z]/,
Lower: /[a-z]/,
Digit: /[0-9]/,
Special: /[^a-z0-9]/i }
array.map do |e|
"Length: #{e.length}, " +
formats.map {|k, v| "#{k}: #{e.scan(v).count}" }.join(', ')
end
#=> ["Length: 11, Upper: 3, Lower: 4, Digit: 2, Special: 2",
# "Length: 8, Upper: 3, Lower: 3, Digit: 2, Special: 0",
# "Length: 8, Upper: 1, Lower: 3, Digit: 3, Special: 1"]
#6
1
Here's a start to help you move into a more OO Ruby script:
这是一个开始帮助您进入更多OO Ruby脚本:
class Foo
attr_reader :str, :str_len, :upper, :lower, :digit, :punctuation
def initialize(str)
@str = str
@str_len = str.length
@upper, @lower, @digit, @punctuation = %w[upper lower digit punct].map { |re| str.scan(/[[:#{re}:]]/).count }
end
def to_s
('"%s": ' % str) +
[:str_len, :upper, :lower, :digit, :punctuation].map { |s|
'%s: %s' % [s.to_s.upcase, instance_variable_get("@#{s}")]
}.join(' ')
end
end
array = ["Amdh#34HB!x", "AzErtY45", "#1A3bhk2"].map { |s| Foo.new(s) }
puts array.map(&:to_s)
Which, when run, outputs:
运行时输出:
"Amdh#34HB!x": STR_LEN: 11 UPPER: 3 LOWER: 4 DIGIT: 2 PUNCTUATION: 2
"AzErtY45": STR_LEN: 8 UPPER: 3 LOWER: 3 DIGIT: 2 PUNCTUATION: 0
"#1A3bhk2": STR_LEN: 8 UPPER: 1 LOWER: 3 DIGIT: 3 PUNCTUATION: 1
The regular expression classes like [[:upper:]]
are POSIX definitions, which help relieve some of the visual noise of a traditional expression's classes. See the Regexp documentation for more information.
像[[:upper:]]这样的正则表达式类是POSIX定义,它有助于减轻传统表达式类的一些视觉噪声。有关更多信息,请参阅Regexp文档。
It can be DRYer but that's an exercise for the student. You should be able to coerce this into something closer to what you want.
它可以是DRYer,但这是学生的练习。你应该能够将它强制为更接近你想要的东西。