Generating a Succession of Strings

Problem

You want to iterate over a series of alphabetically-increasing strings as you would over a series of numbers.

Solution

If you know both the start and end points of your succession, you can simply create a range and use Range#each, as you would for numbers:

	('aa'..'ag').each { |x| puts x }
	# aa
	# ab
	# ac
	# ad
	# ae
	# af
	# ag

The method that generates the successor of a given string is String#succ. If you don't know the end point of your succession, you can define a generator that uses succ, and break from the generator when you're done.

	def endless_string_succession(start)
	 while true
	 yield start
	 start = start.succ
	 end
	end

This code iterates over an endless succession of strings, stopping when the last two letters are the same:

	endless_string_succession('fol') do |x|
	 puts x
	 break if x[-1] == x[-2]
	end
	# fol
	# fom
	# fon
	# foo

 

Discussion

Imagine a string as an odometer. Each character position of the string has a separate dial, and the current odometer reading is your string. Each dial always shows the same kind of character. A dial that starts out showing a number will always show a number. A character that starts out showing an uppercase letter will always show an uppercase letter.

The string succession operation increments the odometer. It moves the rightmost dial forward one space. This might make the rightmost dial wrap around to the beginning: if that happens, the dial directly to its left is also moved forward one space. This might make that dial wrap around to the beginning, and so on:

	'89999'.succ # => "90000"
	'nzzzz'.succ # => "oaaaa"

When the leftmost dial wraps around, a new dial is added to the left of the odometer. The new dial is always of the same type as the old leftmost dial. If the old leftmost dial showed capital letters, then so will the new leftmost dial:

	'Zzz'.succ # => "AAaa"

Lowercase letters wrap around from "z" to "a". If the first character is a lowercase letter, then when it wraps around, an "a" is added on to the beginning of the string:

	'z'.succ # => "aa"
	'aa'.succ # => "ab"
	'zz'.succ # => "aaa"

Uppercase letters work in the same way: "Z" becomes "A". Lowercase and uppercase letters never mix.

	'AA'.succ # => "AB"
	'AZ'.succ # => "BA"
	'ZZ'.succ # => "AAA"
	'aZ'.succ # => "bA"
	'Zz'.succ # => "AAa"

Digits in a string are treated as numbers, and wrap around from 9 to 0, just like a car odometer.

	'foo19'.succ # => "foo20"
	'foo99'.succ # => "fop00"
	'99'.succ # => "100"
	'9Z99'.succ # => "10A00"

Characters other than alphanumerics are not incremented unless they are the only characters in the string. They are simply ignored when calculating the succession, and reproduced in the same positions in the new string. This lets you build formatting into the strings you want to increment.

	'10-99'.succ # => "11-00"

When nonalphanumerics are the only characters in the string, they are incremented according to ASCII order. Eventually an alphanumeric will show up, and the rules for strings containing alphanumerics will take over.

	'a-a'.succ # => "a-b"
	'z-z'.succ # => "aa-a"
	'Hello!'.succ # => "Hellp!"
	%q{'zz'}.succ # => "'aaa'"
	%q{z'zz'}.succ # => "aa'aa'"
	'$$$$'.succ # => "$$$%"
	s = '!@-'
	13.times { puts s = s.succ }
	 # !@.
	 # !@/
	 # !@0
	 # !@1
	 # !@2
	 # …
	 # !@8
	 # !@9
	 # !@10

There's no reverse version of String#succ. Matz, and the community as a whole, think there's not enough demand for such a method to justify the work necessary to handle all the edge cases. If you need to iterate over a succession of strings in reverse, your best bet is to transform the range into an array and iterate over that in reverse:

	("a".."e").to_a.reverse_each { |x| puts x }
	 # e
	 # d
	 # c
	 # b
	 # a

 

See Also

  • Recipe 2.15, "Generating a Sequence of Numbers"
  • Recipe 3.4, "Iterating Over Dates"


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net