in reply to Re^3: Convert a string into a hash
in thread Convert a string into a hash

That was the other aproach I thought, but the requirement changed to pick only the best token from $list, which is loaded many times based on some external events, while %code is initialized once.

Based on the previous, I decided to keep that part, and now I want to rewrite:

my $best = 14; # actually, last value from $tokens. for my $b (split /,/, $list) { $best = $b if $code{$b} < $code{$best}; }

Update: syntax error, extra ")"...

Replies are listed 'Best First'.
Re^5: Convert a string into a hash
by Marshall (Canon) on Aug 18, 2009 at 17:47 UTC
    but the requirement changed to pick only the best token from $list

    Can you define what "best" means?
    Give a text description and then several examples.

      There is a list of available tokens, where one token is "better" than other if it appears previously on that ranking list. From the tokens list given in the first post of this thread, 4 is "better" than 13, and 15 is the "best" of that three tokens.

      The list of tokens is defined once at startup, based on config and user params.

      There is another list from where I must select the "best" token. This list changes on each iteration based on input.

      ... and the requirement changed again!!! on each list from the input can appear other tokens than the valid ones(from tokens list), so I need to discard them. If none of the input's tokens is on the ranking, I must take a default one (the last from the ranking, better than nothing).

      Constructing the hash once for the ranking:

      #!perl -w use strict; my $tokens = "32,15,4,72,13,28,14"; my %ranking = do { my $i; map { $_ => $i++ } split /,/, $tokens }; my ($default) = ($tokens =~ m/,(\d+)$/); # last token while (my $list = <DATA>) { chomp $list; my $best = $default; for my $b (split /,/, $list) { next unless defined $ranking{$b}; $best = $b if $ranking{$b} < $ranking{$best}; } print "$best\n"; } __DATA__ 4,13,15 4,50,15,13 50,60,70

      Using your approach, where the hash must be constructed for each input line:

      #!perl -w use strict; my $tokens = "32,15,4,72,13,28,14"; my @ranking = split (/,/, $tokens); my $default = $ranking[$#ranking]; # last token while (my $list = <DATA>) { chomp $list; my %list = map {$_ => 1} split /,/, $list; my $best = ( grep {$list{$_}} @ranking )[0] || $default; print "$best\n"; } __DATA__ 4,13,15 4,50,15,13 50,60,70

      Both examples should print 15, 15 and 14.

      So, which approach is better? Speed is not an issue (actually, input is retrieved from an external website, one html page, one data row of tokens). Size of tokens lists (initial and input) is not so long at the time (less than 15 values).

      A better (obfuscated) way to write the for of the first program? The next unless defined condition could be removed if the $i variable from the ranking population is initialized with a high value and then decrease the counter on each iteration of the map, but a message will be displayed because of use warnings.

        There is nothing wrong with either approach. A few comments:
        - I would forget about do{}, seldom is that needed in code like this. If there is some need to restrict the scope of my $i, that points to some other problem in the overall code.
        - I would use slice (@ranking)[-1] instead of using something like $ranking[$#ranking].
        - I started $i at 1 to avoid any ambiguity about undef vs zero value.

        Have fun! you are on your way with several ideas that produce correct results. All will run WAY fast enough for your application.

        #!/usr/bin/perl -w use strict; my $ranking_string = "32,15,4,72,13,28,14"; my @ranking = split (/,/, $ranking_string); my $i =1; my %ranking_hash = map {$_ => $i++} @ranking; my $default_best_num = (@ranking)[-1]; while (my $list = <DATA>) { chomp $list; my $best_num = $default_best_num; foreach my $num (split /,/, $list) { next unless $ranking_hash{$num}; $best_num = $num if $ranking_hash{$num} < $ranking_hash{$best_num +}; } print "$best_num\n"; } #prints: #15 #15 #14 __DATA__ 4,13,15 4,50,15,13 50,60,70
        The requirements for this app seem to be a moving target. I thought I'd demo a completely different approach for you. I'm not recommending this for your current requirements, but sounds like something may pop up soon that might need this. This shows "hey, 15 is the "best" one, but what is second best one", etc. This uses the very powerful sort functions of Perl.
        #!/usr/bin/perl -w use strict; my $ranking_string = "32,15,4,72,13,28,14"; my @ranking = split (/,/, $ranking_string); my $i =1; my %ranking_hash = map {$_ => $i++} @ranking; my $default_best_num = (@ranking)[-1]; while (my $line = <DATA>) { chomp $line; my @ranking = sort by_min_ranking grep {$ranking_hash{$_}} (split /,/, $line); if (@ranking){print "@ranking - first one is \"best\"\n"} else {print "$default_best_num - default used\n"} } sub by_min_ranking { $ranking_hash{$a} <=> $ranking_hash{$b} } #prints: #15 4 13 - first one is "best" #15 4 13 - first one is "best" #14 - default used