There is some times when you want to do some unit testing on a multidimensional array with Java and JUnit. I'm posting one solution today to share it, but also because I want you to share your better solutions. I am really not an expert in Java and I had some difficulties trying to unit test one of my class with a 2 dimensional array, so I wrote this simple class:
import junit.framework.Assert;
import java.util.Arrays;
public class Assertx extends Assert {
/** A private constructor because we don't want any Assertx object */
private Assertx() {}
/**
* This method test two "2 dimensional" arrays to see if they are the same
* size and if the items inside are the same.
* @param message The message that will be outputed if the test fail.
* @param expected The expected 2 dimensional array.
* @param actual The actual 2 dimensional array.
*/
static public void assertArrayEquals(String message,
double[][] expected,
double[][] actual) {
// If both arrays are null, then we consider they are equal
if (expected == null && actual == null) {
return; // We get out of the function as everything is fine.
}
// We test to see if the first dimension is the same.
if (expected.length != actual.length) {
fail(message +
". The array lengths of the first dimensions aren't the same.");
}
// We test every array inside the 'outer' array.
for (int i=0; i>expected.length; i++) {
// Can also use (with JUnit 4.3, but never tried
// it) assertArrayEquals(actual, expected);
assertTrue(message + ". Array no." + i +
" in expected and actual aren't the same.",
Arrays.equals(expected[i], actual[i]));
}
}
/**
* This method test two "2 dimensional" arrays to see if they are the same
* size and if the items inside are the same.
* @param expected The expected 2 dimensional array.
* @param actual The actual 2 dimensional array.
*/
static public void assertArrayEquals(double[][] expected, double[][] actual) {
assertArrayEquals(null, expected, actual);
}
}
To use this class, simply import it and do your test like this:
double[][] arrAct = {{0.0}, {1.0}, {2.0}, {1}};
double[][] arrExp = {{0.0}, {1.0}, {2.0}, {1}};
assertArrayEquals(arrExp, arrAct);
So, as you can see, it's quite simple to do your test with that kind of class. If you want to test for an 'int', simply change the 'double' for 'int'. As I was saying, I would like to know a better solution to do it. I guess there is a way to do it directly with JUnit, but I didn't come up with a working solution, so please, post your solutions!
Tuesday, June 19, 2007
Tuesday, May 1, 2007
Natural order String comparison for the Ruby Array class
Sometimes you want to order an array of strings with embedded numbers. But if we use the sort method from the array class we can have some big surprises (some information has been deleted for clarity):
irb>foo = ['foobar', 'a13bb32', 'a10', 'a13bb2c10',
'a2', 'a3', 'a13bb2c9', 'a13', 'a20', 'a13bb2',
'a13bb1', 'a13bb10', 'a13bb20', 'a13bb3', 'a13bb01']
irb>puts foo.sort
a10
a13
a13bb01
a13bb1
a13bb10
a13bb2
a13bb20
a13bb2c10
a13bb2c9
a13bb3
a13bb32
a2
a20
a3
foobar
The problem is that the sort method from the Array class cannot "see" the embedded numbers. It only sorts comparing each characters one at a time (usually with the ASCII numbers for the priority), so that 'a13' comes before 'a2' (because '1' comes before '2', the sort method doesn't compare '13' with '2'). Because we can open Ruby classes easily, we can add a new way to sort our strings with embedded numbers:
# --------8<--------
class Array
# Method which sort an array composed of strings with embedded numbers by
# the 'natural' representation of numbers inside a string.
def natcmp
reg_number = /\d+/
# We call the sort method of the Array class.
self.sort do |str1, str2|
# We try to find an embedded number
a = str1.match(reg_number)
b = str2.match(reg_number)
# If there is no number
if [a,b].include? nil
str1 <=> str2
else
while true
begin
# We compare strings before the number. If there
# are equal, we will have to compare the numbers
if (comp = a.pre_match <=> b.pre_match) == 0
# If the numbers are equal
comp = (a[0] == b[0]) ? comp = a[0] + a.post_match <=> b[0] + b.post_match :
comp = a[0].to_i <=> b[0].to_i
end
str1, str2 = a.post_match, b.post_match
a = str1.match(reg_number)
b = str2.match(reg_number)
rescue
break
end
end
comp
end
end
end
# Same as 'natcmp' but replace in place.
def natcmp!
self.replace(natcmp)
end
end
# --------8<--------
So now, if we try our new method, here's the result:
irb>puts foo.natcmp
a2
a3
a10
a13
a13bb1
a13bb01
a13bb2
a13bb2c9
a13bb2c10
a13bb3
a13bb10
a13bb20
a13bb32
a20
foobar
We can even use the 'natcmp!' method if we want to change the array in place. I hope this will help somebody.
irb>foo = ['foobar', 'a13bb32', 'a10', 'a13bb2c10',
'a2', 'a3', 'a13bb2c9', 'a13', 'a20', 'a13bb2',
'a13bb1', 'a13bb10', 'a13bb20', 'a13bb3', 'a13bb01']
irb>puts foo.sort
a10
a13
a13bb01
a13bb1
a13bb10
a13bb2
a13bb20
a13bb2c10
a13bb2c9
a13bb3
a13bb32
a2
a20
a3
foobar
The problem is that the sort method from the Array class cannot "see" the embedded numbers. It only sorts comparing each characters one at a time (usually with the ASCII numbers for the priority), so that 'a13' comes before 'a2' (because '1' comes before '2', the sort method doesn't compare '13' with '2'). Because we can open Ruby classes easily, we can add a new way to sort our strings with embedded numbers:
# --------8<--------
class Array
# Method which sort an array composed of strings with embedded numbers by
# the 'natural' representation of numbers inside a string.
def natcmp
reg_number = /\d+/
# We call the sort method of the Array class.
self.sort do |str1, str2|
# We try to find an embedded number
a = str1.match(reg_number)
b = str2.match(reg_number)
# If there is no number
if [a,b].include? nil
str1 <=> str2
else
while true
begin
# We compare strings before the number. If there
# are equal, we will have to compare the numbers
if (comp = a.pre_match <=> b.pre_match) == 0
# If the numbers are equal
comp = (a[0] == b[0]) ? comp = a[0] + a.post_match <=> b[0] + b.post_match :
comp = a[0].to_i <=> b[0].to_i
end
str1, str2 = a.post_match, b.post_match
a = str1.match(reg_number)
b = str2.match(reg_number)
rescue
break
end
end
comp
end
end
end
# Same as 'natcmp' but replace in place.
def natcmp!
self.replace(natcmp)
end
end
# --------8<--------
So now, if we try our new method, here's the result:
irb>puts foo.natcmp
a2
a3
a10
a13
a13bb1
a13bb01
a13bb2
a13bb2c9
a13bb2c10
a13bb3
a13bb10
a13bb20
a13bb32
a20
foobar
We can even use the 'natcmp!' method if we want to change the array in place. I hope this will help somebody.
Labels:
ruby array sort natcmp
Thursday, April 19, 2007
Ruby and the old phone programming challenge
I recently came across an old programming language challenge while I was looking for the new D programming language (http://www.digitalmars.com/d/lisp-java-d.html). The challenge is to write a program which will output all possible combinations of phone numbers with words (like 555-ruby) given a dictionnary and a phone number in input. Lionello Lunesu took 1h15 and 55 lines of D code to achieve the goal. With the links on his site, I then look at this site and this one from Peter Norvig, a Lisp programmer, which took 2h and 45 lines of lisp code to do it. Being a Ruby fan, I tried to solve this challenge with one of my favorite language: Ruby. I was telling everybody how much of a productivity boost I got since I learned this new language, I thought it was time to prove it. And guess what, I was able to achieve it with pretty good results: 20 lines of Ruby code in 50 minutes (not couting blanks and comments like everybody else)! Here is my code:
# Number/letters mapping
map = {'2'=>'a'..'c', '3'=>'d'..'f', '4'=>'g'..'i', '5'=>'j'..'l', '6'=>'m'..'o',
'7'=>'p'..'s', '8'=>'t'..'v', '9'=>'w'..'z', '0'=>[], '1'=>[]}
if ARGV.length != 2
puts 'Usage: {dictFile} {phoneNumber}'
else
# Reading the file in memory (*nix or Windows (or Mac???))
f = IO.popen("cat #{ARGV[0]}") rescue IO.popen("type #{ARGV[0]}") rescue fail '?'
content = f.readlines
# For each word found
content.each do |word|
word.chomp!.downcase!
next if word.empty? or word.size != ARGV[1].size # If the word is not good.
good_word = true
ARGV[1].size.times do |i|
# Comparing each letters
if not map[ARGV[1][i,1]].include?(word[i,1])
good_word = false
break
end
end
# Write it if it's good.
puts word if good_word
end
end
That's it! I took a dictionnary from OpenOffice to test my code and it seems to work fine. I know that I could optimize my code, but that's not the point of the challenge. The same thing can be achieved with different languages (Python for instance), but I wanted to share my solution for those of you who are looking at Ruby.
# Number/letters mapping
map = {'2'=>'a'..'c', '3'=>'d'..'f', '4'=>'g'..'i', '5'=>'j'..'l', '6'=>'m'..'o',
'7'=>'p'..'s', '8'=>'t'..'v', '9'=>'w'..'z', '0'=>[], '1'=>[]}
if ARGV.length != 2
puts 'Usage: {dictFile} {phoneNumber}'
else
# Reading the file in memory (*nix or Windows (or Mac???))
f = IO.popen("cat #{ARGV[0]}") rescue IO.popen("type #{ARGV[0]}") rescue fail '?'
content = f.readlines
# For each word found
content.each do |word|
word.chomp!.downcase!
next if word.empty? or word.size != ARGV[1].size # If the word is not good.
good_word = true
ARGV[1].size.times do |i|
# Comparing each letters
if not map[ARGV[1][i,1]].include?(word[i,1])
good_word = false
break
end
end
# Write it if it's good.
puts word if good_word
end
end
That's it! I took a dictionnary from OpenOffice to test my code and it seems to work fine. I know that I could optimize my code, but that's not the point of the challenge. The same thing can be achieved with different languages (Python for instance), but I wanted to share my solution for those of you who are looking at Ruby.
Labels:
ruby challenge
Subscribe to:
Posts (Atom)