J'ai un tableau trié:
[
'FATAL <error title="Request timed out.">',
'FATAL <error title="Request timed out.">',
'FATAL <error title="There is insufficient system memory to run this query.">'
]
Je voudrais obtenir quelque chose comme ça, mais cela ne doit pas être un hachage:
[
{:error => 'FATAL <error title="Request timed out.">', :count => 2},
{:error => 'FATAL <error title="There is insufficient system memory to run this query.">', :count => 1}
]
Le code suivant imprime ce que vous avez demandé. Je vous laisse décider comment utiliser réellement pour générer le hachage que vous recherchez:
# sample array
a=["aa","bb","cc","bb","bb","cc"]
# make the hash default to 0 so that += will work correctly
b = Hash.new(0)
# iterate over the array, counting duplicate entries
a.each do |v|
b[v] += 1
end
b.each do |k, v|
puts "#{k} appears #{v} times"
end
Note: Je viens de remarquer que vous avez dit que le tableau était déjà trié. Le code ci-dessus ne nécessite pas de tri. L'utilisation de cette propriété peut produire un code plus rapide.
Vous pouvez le faire très succinctement (une ligne) en utilisant inject
:
a = ['FATAL <error title="Request timed out.">',
'FATAL <error title="Request timed out.">',
'FATAL <error title="There is insufficient ...">']
b = a.inject(Hash.new(0)) {|h,i| h[i] += 1; h }
b.to_a.each {|error,count| puts "#{count}: #{error}" }
Produira:
1: FATAL <error title="There is insufficient ...">
2: FATAL <error title="Request timed out.">
Si vous avez un tableau comme celui-ci:
words = ["aa","bb","cc","bb","bb","cc"]
où vous devez compter les éléments en double, une solution sur une ligne est:
result = words.each_with_object(Hash.new(0)) { |Word,counts| counts[Word] += 1 }
Une approche différente des réponses ci-dessus, en utilisant Enumerable # group_by .
[1, 2, 2, 3, 3, 3, 4].group_by(&:itself).map { |k,v| [k, v.count] }.to_h
# {1=>1, 2=>2, 3=>3, 4=>1}
Décomposer cela en ses différents appels de méthode:
a = [1, 2, 2, 3, 3, 3, 4]
a = a.group_by(&:itself) # {1=>[1], 2=>[2, 2], 3=>[3, 3, 3], 4=>[4]}
a = a.map { |k,v| [k, v.count] } # [[1, 1], [2, 2], [3, 3], [4, 1]]
a = a.to_h # {1=>1, 2=>2, 3=>3, 4=>1}
Enumerable#group_by
a été ajouté dans Ruby 1.8.7.
Qu'en est-il des éléments suivants:
things = [1, 2, 2, 3, 3, 3, 4]
things.uniq.map{|t| [t,things.count(t)]}.to_h
Cela semble plus propre et plus descriptif de ce que nous essayons réellement de faire.
Je soupçonne qu'il fonctionnerait également mieux avec de grandes collections que celles qui itèrent sur chaque valeur.
Test de performance de référence:
a = (1...1000000).map { Rand(100)}
user system total real
inject 7.670000 0.010000 7.680000 ( 7.985289)
array count 0.040000 0.000000 0.040000 ( 0.036650)
each_with_object 0.210000 0.000000 0.210000 ( 0.214731)
group_by 0.220000 0.000000 0.220000 ( 0.218581)
C'est donc un peu plus rapide.
Personnellement, je le ferais de cette façon:
# myprogram.rb
a = ['FATAL <error title="Request timed out.">',
'FATAL <error title="Request timed out.">',
'FATAL <error title="There is insufficient system memory to run this query.">']
puts a
Exécutez ensuite le programme et dirigez-le vers uniq -c:
Ruby myprogram.rb | uniq -c
Sortie:
2 FATAL <error title="Request timed out.">
1 FATAL <error title="There is insufficient system memory to run this query.">
Depuis Ruby> = 2.2, vous pouvez utiliser itself
: array.group_by(&:itself).transform_values(&:count)
Avec plus de détails:
array = [
'FATAL <error title="Request timed out.">',
'FATAL <error title="Request timed out.">',
'FATAL <error title="There is insufficient system memory to run this query.">'
];
array.group_by(&:itself).transform_values(&:count)
=> { "FATAL <error title=\"Request timed out.\">"=>2,
"FATAL <error title=\"There is insufficient system memory to run this query.\">"=>1 }
a = [1,1,1,2,2,3]
a.uniq.inject([]){|r, i| r << { :error => i, :count => a.select{ |b| b == i }.size } }
=> [{:count=>3, :error=>1}, {:count=>2, :error=>2}, {:count=>1, :error=>3}]
Si vous souhaitez l'utiliser souvent, je vous suggère de le faire:
# lib/core_extensions/array/duplicates_counter
module CoreExtensions
module Array
module DuplicatesCounter
def count_duplicates
self.each_with_object(Hash.new(0)) { |element, counter| counter[element] += 1 }.sort_by{|k,v| -v}.to_h
end
end
end
end
Chargez-le avec
Array.include CoreExtensions::Array::DuplicatesCounter
Et puis utilisez de n'importe où avec juste:
the_ar = %w(a a a a a a a chao chao chao hola hola mundo hola chao cachacho hola)
the_ar.duplicates_counter
{
"a" => 7,
"chao" => 4,
"hola" => 4,
"mundo" => 1,
"cachacho" => 1
}
Les versions Ruby> = 2.7 auront Enumerable # tally .
par exemple:
["a", "b", "c", "b"].tally
# => {"a"=>1, "b"=>2, "c"=>1}
Voici l'exemple de tableau:
a=["aa","bb","cc","bb","bb","cc"]
{'bb' => ['bb', 'bb']}
res = a.uniq.inject ({}) {| accu, uni | accu.merge ({uni => a.select {| i | i == uni}})} {"aa" => ["aa"], "bb" => ["bb", "bb", "bb"], "cc" => ["cc", "cc"]}
Maintenant, vous pouvez faire des choses comme:
res['aa'].size
Implémentation simple:
(errors_hash = {}).default = 0
array_of_errors.each { |error| errors_hash[error] += 1 }