3

I have this array:

@a = ["P1 - D", "P3 - M", "P1 - D", "P1 - M", "P1 - D", "P1 - D", "P1 - D", "P1 - D", "P1 - D", "P1 - D", "P1 - D", "P1 - D", "P1 - M", "P2 - D", "P2 - D", "P2 - D", "P2 - M", "P2 - M", "P3 - D", "P3 - D", "P - D", "P1 - M", "P - D", "P - D", "Post - D", "S1 - D", "P1 - M"]

Every string is based on Page# - Device. So P1 - D is: Page1 - Desktop & P3 - M is: Page3 - Mobile

How can find how many of the strings inside array has D or M?

Rubioli
  • 685
  • 6
  • 34

5 Answers5

4
a.group_by { |e| e[-1] }.each_with_object({}) { |(k, v), hash| hash[k] = v.count }
#=> {"D"=>20, "M"=>7}

Steps:

groups = a.group_by { |e| e[-1] }
# {
#   "D"=> ["P1 - D", "P1 - D", "P1 - D", "P1 - D", "P1 - D", "P1 - D", "P1 - D", "P1 - D", "P1 - D", "P1 - D", "P2 - D", "P2 - D", "P2 - D", "P3 - D", "P3 - D", "P - D", "P - D", "P - D", "Post - D", "S1 - D"],
#   "M"=> ["P3 - M", "P1 - M", "P1 - M", "P2 - M", "P2 - M", "P1 - M", "P1 - M"]
# }

group_counts = groups.each_with_object({}) { |(k, v), hash| hash[k] = v.count }
#=> {"D"=>20, "M"=>7}

group_counts['M']
#=> 20

edit

With Ruby 2.4+ you can make use of Hash#transform_values (credits go to Alex Golubenko :)):

a.group_by { |e| e[-1] }.transform_values(&:size)
#=> {"D"=>20, "M"=>7}
Community
  • 1
  • 1
Andrey Deineko
  • 51,333
  • 10
  • 112
  • 145
3

Possible solution:

@a.group_by { |e| e[-1] }.map {|e, a| [e, a.size]}.to_h
=> {"D"=>20, "M"=>7}
Ilya
  • 13,337
  • 5
  • 37
  • 53
2
@a = ["P1 - D", "P3 - M", "P1 - D", "P1 - M", "P1 - D", "P1 - D", "P1 - D", "P1 - D", "P1 - D", "P1 - D", "P1 - D", "P1 - D", "P1 - M", "P2 - D", "P2 - D", "P2 - D", "P2 - M", "P2 - M", "P3 - D", "P3 - D", "P - D", "P1 - M", "P - D", "P - D", "Post - D", "S1 - D", "P1 - M"]
@a.count { |string| string.match(/D|M/) }
#=> 27
Oleksandr Holubenko
  • 4,310
  • 2
  • 14
  • 28
  • I like this best because it's efficient and reads best. Having `count` up-front tells the reader what you are doing. In Ruby v2.4+ you can use `string.match? /D\M/`, which arguably reads even better. You could of course use `/[DM]/` instead of `/D}M/`. I suggest you delete the first line. Since the OP defined `@a` it's redundant and it makes your answer less visually attractive. – Cary Swoveland Feb 27 '17 at 22:21
1
@a.select { |word| word.include?('D') || word.include?('M') }.size
# => 27
Kris
  • 19,188
  • 9
  • 91
  • 111
1

If you just want the total, you could use :

@a.grep(/D|M/).count
#=> 27

If you want subtotals, this should be the most efficient method, since it doesn't create any temporary array :

@a.each_with_object(Hash.new(0)) { |string, count| count[string[-1]] += 1 }
#=> {"D"=>20, "M"=>7}
Eric Duminil
  • 52,989
  • 9
  • 71
  • 124