http://qs321.pair.com?node_id=1207805


in reply to Illegal division by zero

You can also split the detection of WINDSOR RIVER from the extraction on the values. Here is my way ...

#!usr/bin/perl use strict; use warnings; use List::Util qw(sum); my @windsordigits; while (my $x = <DATA>){ next unless $x =~ /WINDSOR\sRIVERSIDE/; push @windsordigits, +(split /,/, $x)[-1]; } die "No 'Windsor digits found in input\n" unless @windsordigits; my $average = sum(@windsordigits) / @windsordigits; print "Average is: $average\n"; __DATA__ CA006139520,"WINDSOR RIVERSIDE, ON CA",2018-01-02,10 CA006139520,"WINDSOR RIVERSIDE, ON CA",2018-01-02,20 CA006139520,"WINDSOR RIVERSIDE, ON CA",2018-01-02,14 CA006139520,"WINDSOR DRIVE, ON CA",2018-01-02,10

I use a regex to match, then if we matched in the regex, split the line on the , character and take the last element or the resulting list using the [-1] offset.

Update

Added a die if no digits are detected in the input.

Cheers,
R.

Pereant, qui ante nos nostra dixerunt!

Replies are listed 'Best First'.
Re^2: Illegal division by zero
by drose2211 (Sexton) on Jan 24, 2018 at 17:27 UTC

    Just curious. If I had multiple names I am attempting to match. Say there is windsor riverside, new york, and philadelphia. Would I need to make additional loops or is it possible to push the digits for the corresponding names into different arrays or would I write that into the same while loop? EDIT: Answered my own question. Just added if statements in the while loop.

      drose2211:

      Sure thing: you can use a hash to hold an array of digit entries for each city you encounter, like this:

      $ cat pm1207805.pl #!usr/bin/perl use strict; use warnings; use Text::CSV; use List::Util qw(sum); use Data::Dumper; my $csv = Text::CSV->new(); my $FH = \*DATA; my %accumulator; # Gather the digits for the cities while (my $row = $csv->getline($FH)) { my $city = $row->[1]; my $digits = $row->[3]; push @{$accumulator{$city}}, $digits; } # What do we have to work with? print "Data:\n", Dumper(\%accumulator), "\n\n"; # Dump our results for my $city (sort keys %accumulator) { my $num_rows = @{$accumulator{$city}}; print "$city: ", sum(@{$accumulator{$city}}) / $num_rows, "\n"; } __DATA__ CA006139520,"WINDSOR RIVERSIDE, ON CA",2018-01-02,10 CA006139520,"WINDSOR RIVERSIDE, ON CA",2018-01-02,20 CA006139520,"WINDSOR RIVERSIDE, ON CA",2018-01-02,14 CA006138520,"NEW YORK",2018-01-02,11 CA006137520,"PHILADELPHIA, ON CA",2018-01-02,23 CA006137520,"PHILADELPHIA, ON CA",2018-01-02,25 CA006138520,"NEW YORK",2018-01-02,13 CA006138520,"NEW YORK",2018-01-02,19

      When you run it, you should get:

      $ perl pm1207805.pl Data: $VAR1 = { 'WINDSOR RIVERSIDE, ON CA' => [ '10', '20', '14' ], 'PHILADELPHIA, ON CA' => [ '23', '25' ], 'NEW YORK' => [ '11', '13', '19' ] }; NEW YORK: 14.3333333333333 PHILADELPHIA, ON CA: 24 WINDSOR RIVERSIDE, ON CA: 14.6666666666667

      ...roboticus

      When your only tool is a hammer, all problems look like your thumb.

      ... multiple names I am attempting to match. ... Just added if statements in the while loop.

      if-statement patches are probably ok for one-off or infrequent runs with a small, stable city-name list. For larger lists of cities or more frequent runs, I think I would go with a database.

      It's also possible to use a regex/hash approach:

      c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -le "my @cities = ('windsor riverside', ' new york ', 'philadelphia',); ;; my $rx_city = build_city_regex(@cities); print $rx_city; ;; my %city_digits; ;; RECORD: for my $record ( 'CA006139520,\"WINDSOR RIVERSIDE, ON CA \",2018-01-02,10', qq{CA006139520,\" NEW YORK , ON CA \",2018-01-02,987\n}, 'CA006139520,\"NEWYORK, ON CA \",2018-01-02,9999', 'CA006139520,\"NEW YORK, ON CA \",2018-01-02,10210', qq{CA006139520,\"PHILADELPHIA, ON CA \",2018-01-02,76\n}, ) { next RECORD unless my ($city, $digits) = $record =~ m{ ($rx_city) .* \b (\d+) \Z }xm +s; push @{ $city_digits{ canonicalize_city($city) } }, $digits } dd \%city_digits; ;; sub build_city_regex { my ($regex) = map qr{ \b (?: $_) \b }xms, join ' | ', map { (my $c = $_) =~ s{ \s+ }'\s+'xmsg; $c; } reverse sort map canonicalize_city($_), @_ ; return $regex; } ;; sub canonicalize_city { my ($city_name) = @_; ;; die qq{bad city: '$city_name'} if $city_name =~ m{ [^[:alpha:] -] }xms; $city_name =~ s{ \A \s+ | \s+ \z }''xmsg; $city_name =~ s{ \s+ }' 'xmsg; $city_name = uc $city_name; ;; return $city_name; } " (?msx-i: \b (?: WINDSOR\s+RIVERSIDE | PHILADELPHIA | NEW\s+YORK) \b ) { "NEW YORK" => [987, 10210], PHILADELPHIA => [76], "WINDSOR RIVERSIDE" => [10], }
      Something like this will work even with large lists (thousands!) of city names. However, as I said, for a sufficiently high size-frequency metric, it's probably better to use a database.


      Give a man a fish:  <%-{-{-{-<