# Need a hash/iteration tutorial...text reading

**URL:** <https://www.ruby-forum.com/t/need-a-hash-iteration-tutorial-text-reading/170559>\
**Category:** Ruby\
**Created:** [June 18, 2009, 10:40pm UTC](https://www.ruby-forum.com/t/need-a-hash-iteration-tutorial-text-reading/170559 "2009-06-18T22:40:27Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![S-redyck](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/c5a1d2/32.png) [@S-redyck](https://www.ruby-forum.com/u/S-redyck)\
**Post date:** [June 18, 2009, 10:40pm UTC](https://www.ruby-forum.com/t/need-a-hash-iteration-tutorial-text-reading/170559/1 "2009-06-18T22:40:27Z")

</div>

I have been working my way through a ruby book (Beginning Ruby) and I  
want to extend on an interesting capability dealing with hashes.

the code:  
text=’’  
line\_count=0  
File.open(“txt.txt”).each do |line|  
line\_count +=1  
text \<\< line  
end

puts “#{line\_count} lines”

total\_charachters=text.length  
puts “#{total\_charachters} charachters”  
sentence\_count=text.split(/.|?|!/).length  
total\_characters\_no\_spaces=text.gsub(/\s+/,"").length  
puts “#{total\_characters\_no\_spaces} without spaces”  
word\_count=text.split.length  
puts “#{word\_count} words in the text and #{sentence\_count} sentences”  
paragraph\_count= text.split(/\n\n/).length  
puts “#{paragraph\_count} paragraphs”  
puts “#{sentence\_count/paragraph\_count} sentences per paragraph on  
avarage”  
puts “#{word\_count/sentence\_count} words per sentence”  
stop\_words= %w{a the by on for of are with just but and to the my has  
some in}  
words=text.scan(/\w+/)  
keywords=words.select{|word| !stop\_words.include?(word)}  
puts “#{((keywords.length.to\_f/words.length.to\_f)\*100).to\_i}% non stop  
words”

this has been a fun code, and I have been running various text files  
through it.

What I want to know is, is it possible to create a has where the key is  
the word, and the value is the number of occurrences of the word in the  
text, and then sort the hash by the values?

---

<div class="post-metadata">

**Author:** ![S-redyck](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/c5a1d2/32.png) [@S-redyck](https://www.ruby-forum.com/u/S-redyck)\
**Post date:** [June 18, 2009, 11:16pm UTC](https://www.ruby-forum.com/t/need-a-hash-iteration-tutorial-text-reading/170559/2 "2009-06-18T23:16:08Z")

</div>

On Thu, Jun 18, 2009 at 10:40 PM, Steven D.  
[removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)wrote:

> ## What I want to know is, is it possible to create a has where the key is the word, and the value is the number of occurrences of the word in the text, and then sort the hash by the values?
> 
> Posted via [http://www.ruby-forum.com/](http://www.ruby-forum.com/).

Super quick and dirty, but should get you started:

words = {}  
File.open(“txt.txt”).each do |line|  
line.split(’ ').each { |w| words.has\_key?(w) ? words[w] += 1 :  
words[w] =  
1 }  
end

words.sort\_by { |e| e[1]}.reverse.each { |k, v| puts “#{k}: #{v}”}

---

<div class="post-metadata">

**Author:** ![S-redyck](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/c5a1d2/32.png) [@S-redyck](https://www.ruby-forum.com/u/S-redyck)\
**Post date:** [June 18, 2009, 11:57pm UTC](https://www.ruby-forum.com/t/need-a-hash-iteration-tutorial-text-reading/170559/3 "2009-06-18T23:57:13Z")

</div>

Steven D. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> What I want to know is, is it possible to create a has where the key is  
> the word, and the value is the number of occurrences of the word in the  
> text, and then sort the hash by the values?

That is called a “histogram” and is one of the most common examples  
(Google is your friend). The sticking point here is what you mean by a  
“word”. If you’re willing to accept a fairly crude definition of this  
notion, then that is an example I develop in the Blocks section of my  
Ruby tutorial chapter here:

[http://www.apeth.com/ruby/02justenoughruby.html](http://www.apeth.com/ruby/02justenoughruby.html)

To sort, add this line:

wds = h.sort {|x,y| x[1] \<=\> y[1]}

Note that the concept “sort a hash” has no real meaning, since a hash is  
not ordered. What you can do is to convert to an array and sort the  
array.

---

<div class="post-metadata">

**Author:** ![S-redyck](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/c5a1d2/32.png) [@S-redyck](https://www.ruby-forum.com/u/S-redyck)\
**Post date:** [June 19, 2009, 12:33am UTC](https://www.ruby-forum.com/t/need-a-hash-iteration-tutorial-text-reading/170559/4 "2009-06-19T00:33:45Z")

</div>

Matt N. wrote:

> Steven D. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:
> 
> > What I want to know is, is it possible to create a has where the key is  
> > the word, and the value is the number of occurrences of the word in the  
> > text, and then sort the hash by the values?
> 
> That is called a “histogram” and is one of the most common examples  
> (Google is your friend). The sticking point here is what you mean by a  
> “word”. If you’re willing to accept a fairly crude definition of this  
> notion, then that is an example I develop in the Blocks section of my  
> Ruby tutorial chapter here:
> 
> [Just Enough Ruby](http://www.apeth.com/ruby/02justenoughruby.html)
> 
> To sort, add this line:
> 
> wds = h.sort {|x,y| x[1] \<=\> y[1]}
> 
> Note that the concept “sort a hash” has no real meaning, since a hash is  
> not ordered. What you can do is to convert to an array and sort the  
> array.  
> Since arrays are key/value (from what I can understand), there are only  
> two part to the array. I thought you couldn’t put a third value in an  
> array.

thanks for the help. I am going to check out the web page.

(Never knew of Histogram. Learn something new every other day or so.)

---

<div class="post-metadata">

**Author:** ![S-redyck](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/c5a1d2/32.png) [@S-redyck](https://www.ruby-forum.com/u/S-redyck)\
**Post date:** [June 19, 2009, 3:09pm UTC](https://www.ruby-forum.com/t/need-a-hash-iteration-tutorial-text-reading/170559/5 "2009-06-19T15:09:46Z")

</div>

Hi –

On Fri, 19 Jun 2009, Matt N. wrote:

> Ruby tutorial chapter here:
> 
> [Just Enough Ruby](http://www.apeth.com/ruby/02justenoughruby.html)
> 
> To sort, add this line:
> 
> wds = h.sort {|x,y| x[1] \<=\> y[1]}

Or, slightly more compact:

wds = h.sort\_by {|x| x[1] }

> Note that the concept “sort a hash” has no real meaning, since a hash is  
> not ordered. What you can do is to convert to an array and sort the  
> array.

In 1.9 hashes are ordered, but by key-insertion order. You can’t  
change the order, so you can’t sort back into a hash (unless you  
create a new hash manually using the sorted order).

David

---

<div class="post-metadata">

**Author:** ![S-redyck](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/c5a1d2/32.png) [@S-redyck](https://www.ruby-forum.com/u/S-redyck)\
**Post date:** [June 19, 2009, 3:10pm UTC](https://www.ruby-forum.com/t/need-a-hash-iteration-tutorial-text-reading/170559/6 "2009-06-19T15:10:59Z")

</div>

Hi –

On Fri, 19 Jun 2009, Steven D. wrote:

> Since arrays are key/value (from what I can understand), there are only  
> two part to the array. I thought you couldn’t put a third value in an  
> array.

It’s more that you sort the hash into an array of two-element arrays,  
and then sort that array. Iterating through an array of two-element  
arrays is similar to iterating through a hash, in the sense that each  
iteration yields two values.

David

---

<div class="post-metadata">

**Author:** ![S-redyck](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/c5a1d2/32.png) [@S-redyck](https://www.ruby-forum.com/u/S-redyck)\
**Post date:** [June 20, 2009, 2:17pm UTC](https://www.ruby-forum.com/t/need-a-hash-iteration-tutorial-text-reading/170559/7 "2009-06-20T14:17:42Z")

</div>

On Fri, Jun 19, 2009 at 3:09 PM, David A. Black[removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
wrote:

> > That is called a “histogram” and is one of the most common examples
> 
> create a new hash manually using the sorted order).  
> IIRC the insertion order is maintained correctly for literals and Hash[]  
> thus  
> Hash[\* a\_hash.sort\_by{ whatever }]  
> should do the trick.

Cheers  
Robert

> 

–  
Toutes les grandes personnes ont d’abord été des enfants, mais peu  
d’entre elles s’en souviennent.

All adults have been children first, but not many remember.

[Antoine de Saint-Exupéry]

---

<div class="post-metadata">

**Author:** ![S-redyck](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/c5a1d2/32.png) [@S-redyck](https://www.ruby-forum.com/u/S-redyck)\
**Post date:** [June 20, 2009, 2:29pm UTC](https://www.ruby-forum.com/t/need-a-hash-iteration-tutorial-text-reading/170559/8 "2009-06-20T14:29:25Z")

</div>

Hi –

On Sat, 20 Jun 2009, Robert D. wrote:

> > > wds = h.sort {|x,y| x[1] \<=\> y[1]}  
> > > change the order, so you can’t sort back into a hash (unless you  
> > > create a new hash manually using the sorted order).
> 
> IIRC the insertion order is maintained correctly for literals and Hash[]  
> thus  
> Hash[\* a\_hash.sort\_by{ whatever }]  
> should do the trick.

You’d want to throw in a flatten(1) to unwrap the inner arrays:

[\*hash.sort\_by {…}.flatten(1)]

David

---

<div class="post-metadata">

**Author:** ![S-redyck](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/c5a1d2/32.png) [@S-redyck](https://www.ruby-forum.com/u/S-redyck)\
**Post date:** [June 20, 2009, 2:42pm UTC](https://www.ruby-forum.com/t/need-a-hash-iteration-tutorial-text-reading/170559/9 "2009-06-20T14:42:17Z")

</div>

On Sat, Jun 20, 2009 at 2:28 PM, David A. Black [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
wrote:

> This message is in MIME format. The first part should be readable text,  
> while the remaining parts are likely unreadable without MIME-aware tools.  
> Ooops, that was yet another mistake, thx for telling me David.
> 
> > IIRC the insertion order is maintained correctly for literals and Hash[]  
> > thus  
> > Hash[\* a\_hash.sort\_by{ whatever }]  
> > should do the trick.
> 
> You’d want to throw in a flatten(1) to unwrap the inner arrays:
> 
> [\*hash.sort\_by {…}.flatten(1)]  
> well spotted.  
> R.
