views:

298

answers:

4

const
states : array [0..49,0..1] of string =
(
('Alabama','AL'),
('Montana','MT'),
('Alaska','AK'),
('Nebraska','NE'),
('Arizona','AZ'),
('Nevada','NV'),
('Arkansas','AR'),
('New Hampshire','NH'),
('California','CA'),
('New Jersey','NJ'),
('Colorado','CO'),
('New Mexico','NM'),
('Connecticut','CT'),
('New York','NY'),
('Delaware','DE'),
('North Carolina','NC'),
('Florida','FL'),
('North Dakota','ND'),
('Georgia','GA'),
('Ohio','OH'),
('Hawaii','HI'),
('Oklahoma','OK'),
('Idaho','ID'),
('Oregon','OR'),
('Illinois','IL'),
('Pennsylvania','PA'),
('Indiana','IN'),
('Rhode Island','RI'),
('Iowa','IA'),
('South Carolin','SC'),
('Kansas','KS'),
('South Dakota','SD'),
('Kentucky','KY'),
('Tennessee','TN'),
('Louisiana','LA'),
('Texas','TX'),
('Maine','ME'),
('Utah','UT'),
('Maryland','MD'),
('Vermont','VT'),
('Massachusetts','MA'),
('Virginia','VA'),
('Michigan','MI'),
('Washington','WA'),
('Minnesota','MN'),
('West Virginia','WV'),
('Mississippi','MS'),
('Wisconsin','WI'),
('Missouri','MO'),
('Wyoming','WY')
);
function getabb(state:string):string;
var
  I:integer;
begin
  for I := 0 to length(states) -1 do
  if lowercase(state) = lowercase(states[I,0]) then
  begin
    result:= states[I,1];
  end;
end;
function getstate(state:string):string;
var
  I:integer;
begin
  for I := 0 to length(states) -1 do
  if lowercase(state) = lowercase(states[I,1]) then
  begin
    result:= states[I,0];
  end;
end;
procedure TForm2.Button1Click(Sender: TObject);
begin
 edit1.Text:=getabb(edit1.Text);
end;

procedure TForm2.Button2Click(Sender: TObject);
begin
 edit1.Text:=getstate(edit1.Text);
end;

end.

Is there a bette way to do this?

+7  A: 

If you're on D2009 or D2010, use a TDictionary<string, string> from Generics.Collections. Declare the array of constants like you have it, then set up your dictionary by putting each pair in to the dictionary. Then just use the dictionary's default property to do your lookups.

Mason Wheeler
I'd suggest two dictionaries, one for each function.
Marcelo Cantos
+2  A: 

Notice that lowercase(a) = lowercase(b) is slower than sameText(a, b).

In addition, you can speed up the procedure further by storing the strings in the array as lower-case only, and then in the look-up routine start with converting the input to lower-case as well. Then you can use the even faster function sameStr(a, b). But of course, when a match is found, you then need to format it by capitalizing the initial letters. This speed-up approach is probably not very important for such a small list of strings. After all, there are not too many states in the US.

Also, you should declare the functions using const arguments, i.e. write

function getabb(const state:string):string;

instead of

function getabb(state:string):string;

(unless you want to change state in the routine).

Finally, you could make the code more compact and readable by omitting the begin and end of the for loops.

Andreas Rejbrand
No way is it the fastest (and it's odd that you start by saying it's the fastest, and then go on to suggest speed-ups). A hash table will be faster. In fact, two hash tables, one for `getabb` and one for `getstate`, dynamically grown at startup till there are no collisions, is even faster. Possibly faster still is a trie structure. But if you really want to max it out, code up the trie pathways directly, which lets you optimise for edge cases like `"Missouri"` and `"Mississippi"`.
Marcelo Cantos
@Marcelo: In principle means that minor modifications might be needed, but that the main idea is OK. But you are right it is not the fastest anyway so I will remove that incorrect part. The first seconds when I read the question I thought the problem was simply to obtain the Nth element of an array, i.e. you insert a number and get a string. And then there would not be any faster method. And somehow this conclusion remained inside my mind even after I had understood the true problem.
Andreas Rejbrand
+7  A: 

Should this kind of data be hard coded?
Wouldn't it be better to use something like a XML file or even just a CSV.

Or Name Value Pairs, i.e. IA=Iowa
then loaded into a TStringList to get

States.Values['IA'] = 'Iowa';

Then you just need to write something to search the Values to work backwards like

//***Untested***
//Use: NameOfValue(States, 'Iowa') = 'IA'

function NameOfValue(const strings: TStrings; const Value: string): string;
var
  i : integer;
  P: Integer;
  S: string;
begin
  for i := 0 to strings.count - 1 do
  begin
    S := strings.ValueFromIndex[i];
    P := AnsiPos(strings.NameValueSeparator, S);
    if (P <> 0) and (AnsiCompareText(Copy(S, 1, P - 1), Value) = 0) then
     begin
      Result := strings.Names[i];
      Exit;
     end;
  end;
  Result := '';
end;

I'm fairly sure its case insensitive too

Christopher Chase
+1; as using a TStringList with name/value pairs is a good solution. I don't understand why you got downvoted by others (it is pretty rude to downvote without leaving a comment).
Jeroen Pluimers
@Christopher Chase: Why should it NOT be hardcoded? The names and abbreviations of the US states have been fixed for quite some time now, and probably aren't going to change in the near future. Do you put your own name in an XML or CSV file, along with your initials? (Not downvoting because it is an answer, although IMO not a good one. It's like adding an RDBMS to your app to store the one and only user's login name and password - needless overhead, file I/O, and code for absolutely no gain.)
Ken White
@Ken White: with respect, I disagree. Mixing code and data is icky. For example, the spelling mistake for `South Carolina` that was mentioned here by dthorpe. If that had been in the executable, a new executable would have to have been delivered, whereas the error might be easier to correct if the data was stored in a separate location, such as a central database. Deployment of large numbers of executables at sites with hundreds of workstations is not something to be undertaken lightly.
cjrh
Future Proofing is the main reason why i would not store data in the code, yes simple spelling mistakes can be changed in code with little fuss. but what if you one day decide you want to export your application to somewhere else in the world, rather than shipping it with a different build, you could just supply the same build with a different 'states' file.
Christopher Chase
Sure you could go to a new location. If you can figure a way to store what you need in a two-letter abbreviation somewhere. But chances are you can't, and you'll need DB changes, data entry field changes, etc. Also with respect, adding an external dependency for absolutely no reason doesn't make sense. What happens if someone deletes your XML or CSV file by mistake? Does your app just not work? Of course not; you'll have hard-coded in something to make sure it still functions. Guess what? That hard-coded something is probably the same list of states and abbrevs that have to be maintained.
Ken White
+1  A: 

I would have your lists sorted. That way you can use a binary search to cut the lookup times down. It all depends on the number of iterations you will be exercising. Around 50 items doesn't seem like much, until your iterating over the list a few thousand times looking for the last item in the list.

Also you should ALWAYS bail from your loops as soon as you get get a match if you know the rest of the list will not match.

Arrays are fine, and depending on how your using the data, you might need to add some of the "territories" that also have abbreviations (PR = PUERTO RICO, GU = GUAM, etc.).

skamradt