Create Hash from Array and Frequency

Create hash from array and frequency

Do as below :

def score( array )
hash = Hash.new(0)
array.each{|key| hash[key] += 1}
hash
end
score([1,2,4,5,4,7]) # => {1=>1, 2=>1, 4=>2, 5=>1, 7=>1}

Or more Rubyish using Enumerable#each_with_object:

def score( array )
array.each_with_object(Hash.new(0)){|key,hash| hash[key] += 1}
end
score([1,2,4,5,4,7]) # => {1=>1, 2=>1, 4=>2, 5=>1, 7=>1}

The reason of why NoMethodError: undefined method '+' for nil:NilClass ?

hash = {} is an empty has,with default value as nil.nil is an instance of Nilclass,and NilClass doesn't have any instance method called #+. So you got NoMethodError.

Look at the Hash::new documentation :

new → new_hash
new(obj) → new_hash

Returns a new, empty hash. If this hash is subsequently accessed by a key that doesn’t correspond to a hash entry, the value returned depends on the style of new used to create the hash. In the first form, the access returns nil. If obj is specified, this single object will be used for all default values. If a block is specified, it will be called with the hash object and the key, and should return the default value. It is the block’s responsibility to store the value in the hash if required.

Array to Hash : words count

The imperative approach you used is probably the fastest implementation in Ruby. With a bit of refactoring, you can write a one-liner:

wf = Hash.new(0).tap { |h| words.each { |word| h[word] += 1 } }

Another imperative approach using Enumerable#each_with_object:

wf = words.each_with_object(Hash.new(0)) { |word, acc| acc[word] += 1 }

A functional/immutable approach using existing abstractions:

wf = words.group_by(&:itself).map { |w, ws| [w, ws.length] }.to_h

Note that this is still O(n) in time, but it traverses the collection three times and creates two intermediate objects along the way.

Finally: a frequency counter/histogram is a common abstraction that you'll find in some libraries like Facets: Enumerable#frequency.

require 'facets'
wf = words.frequency

frequency of objects in an array using Ruby

Your code isn't bad, but it is inefficient. If I were you I would seek a solution that iterates through your array only once, like this:

balls = [m1, m2, m3, m4]
most_idx = nil

groups = balls.inject({}) do |hsh, ball|
hsh[ball.color] = [] if hsh[ball.color].nil?
hsh[ball.color] << ball

most_idx = ball.color if hsh[most_idx].nil? || hsh[ball.color].size > hsh[most_idx].size
hsh
end

groups[most_idx] # => [m1,m2,m4]

This does basically the same thing as group_by, but at the same time it counts up the groups and keeps a record of which group is largest (most_idx).

Using Hash Table to count the frequencies - Self Made

Yes, both tasks can be done using both Hash tables and BSTs, with linear space required.

Both hash table and binary search tree can implement a map interface, where you can fast look by a key, and link it to a value.

You can use this map interface to implement a histogram, which maps from your keys to an integer.

You iterate the array, and for each element, look for it in the map (as a key). If this key exists, get the value, and increase it by one.

Otherwise, insert this new element to the map with value 1.

When this is done, you have a map fulfilling requirement 1.

To fulfill the second requirement, simply iterate the map and find the key associated with the highest value, and return it.

This is done with linear extra space needed, and is done in O(n) when the map is based on a hash table, and O(nlogn) when it's based on a BST. (all average cases, for BST, using a self balancing BST time complexity can be O(nlogn) worst case).

C++11 code with hash map implementing the map interface

#include <iostream>
#include <unordered_map>
using namespace std;

int main() {
int array[] = {1,9,9,7,5,4,1,2,0,1,0};
std::unordered_map<int,int> histogram;
for (int x : array) {
auto in_map = histogram.find(x);
if (in_map == histogram.end()) {
histogram[x] = 1;
} else {
++(in_map->second);
}
}
int most_occurances_element = -1;
int most_occurances = -1;
for (const auto& kv : histogram) {
if (kv.second > most_occurances) {
most_occurances_element = kv.first;
most_occurances = kv.second;
}
}
std::cout << "Most frequent element " << most_occurances_element << " with "
<< most_occurances << " occurances.";
return 0;
}

Be aware that this answer refers to integers (or other enumerable types) as the elements. For floating point the answer could be completely different based on various factors (definition of "equality", source of the elements, ...)

How to declare and assign a hash with count of occurrences in one statement

Can roll it in a subroutine ... or use the ones provided in libraries

For simple and fast element-frequency counter there is List::MoreUtils::frequency

use List::MoreUtils qw(frequency);

my %freq_count = frequency LIST;

The LIST is any dynamically generated list (that lemmatizer(...) here), or an array variable.

If you'd like a possibility to fine-tune what is counted there is count_by in List::UtilsBy

use List::UtilsBy qw(count_by);

my %freq_count = count_by { $_ } LIST;

Again, LIST is any dynamically produced list or an array variable, and in your case that would be lemmatizer(...) (which returns a list).

The frequency count is returned for values to which the code inside the block evaluates; each element in turn is provided in $_. So with lone $_ the count is for elements themselves.

Both List::MoreUtils and List::UtilsBy probably need be installed from CPAN.

Create array of objects from hash keys and values

@popular.map { |k, v| { code: k, frequency: v } }

This will produce an array of Hashes. If you need an array of models, replace the inner {...} with an appropriate constructor.

Efficiency of Ruby code: hash of month + frequency into formatted sorted array

['September 2016', 'July 2017', 'September 2016', 'July 2017']
.group_by { |e| e } # .group_by(&:itself) since Ruby2.3
.sort_by { |k, _| Date.parse(k) }
.reverse
.map { |k, v| "#{k} (#{v.count})" }

#⇒ [
# [0] "July 2017 (2)",
# [1] "September 2016 (2)"
# ]


Related Topics



Leave a reply



Submit