Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Filtering array of strings and numbers

by nysus (Parson)
on Apr 29, 2016 at 15:28 UTC ( [id://1161884]=perlquestion: print w/replies, xml ) Need Help??

nysus has asked for the wisdom of the Perl Monks concerning the following question:

I'm stuck on what I think should be a simple problem. I want to filter out elements from an array. I don't know if they array contains strings or numbers or both. And I don't know if the value I want to filter out is a string or number:

my $string_or_number; my @filtered = grep { $string_or_number ne $_ } @strings_or_numbers;

What's the best way to accomplishing this without throwing an error if the wrong equality operator is used?

$PM = "Perl Monk's";
$MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate";
$nysus = $PM . ' ' . $MCF;
Click here if you love Perl Monks

Replies are listed 'Best First'.
Re: Filtering array of strings and numbers
by Athanasius (Archbishop) on Apr 29, 2016 at 15:39 UTC

    Hello nysus,

    Since you’re testing for equality only, the straightforward solution is to stringify both terms in the comparison, thereby guaranteeing that you always compare a string with a string:

    #! perl use strict; use warnings; use Data::Dump; my $string_or_number = 123; my @strings_or_numbers = ('abc', 123, 'ab4', 456); my @filtered = grep { "$string_or_number" ne "$_" } @strings_or_numbe +rs; dd \@filtered;

    Output:

    1:37 >perl 1612_SoPW.pl ["abc", "ab4", 456] 1:37 >

    Of course, this assumes that neither $string_or_number nor any of the elements of @strings_or_numbers are ever undef.

    Update: As AnomalousMonk and Laurent_R have pointed out below, the stringwise equality operators eq and ne implicity stringify their operands, so grep { "$string_or_number" ne "$_" } can be simplified to grep { $string_or_number ne $_ }.

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Update: The undef issue that Athanasius pointed out can be avoided by adding a defined check in the grep:

      grep {defined && $string_or_number ne $_} @strings_or_numbers;

      /Update

      ++. This is so elegantly simple it hurts :)

      I was just in a situation the other day where I had to do the same thing as the OP, and it didn't even cross my mind to stringify like that.

      These simple tricks are often the ones that one doesn't think about or forgets about when their head is wrapped around larger problems.

      Except perhaps for purposes of enhanced readability/maintainability, is the explicit stringification ever needed? Doesn't  ne or any other stringwise comparator implicitly stringify everything it operates on, including references (but with due deference to undef)? (Granted, stringifiying a reference for comparison purposes is usually useless, but that's another discussion.)

      c:\@Work\Perl\monks>perl -wMstrict -le "use warnings; use strict; ;; use Data::Dump qw(dd); ;; my @stuff = ('abc', 123, 'ab4', '123', 456, [ 123 ], { 123 => 'x', x +=> 123 }); ;; my $thing = 123; my @filtered = grep { $thing ne $_ } @stuff; dd \@filtered; ;; $thing = '123'; @filtered = grep { $thing ne $_ } @stuff; dd \@filtered; " ["abc", "ab4", 456, [123], { 123 => "x", x => 123 }] ["abc", "ab4", 456, [123], { 123 => "x", x => 123 }]

      OT: I sometimes see what one might call "super stringification" in code that often seems to originate from biological users, e.g.:
          my $filename = '...';
          open my $fh, '<', "$filename" or die "...";
      Can anyone comment on the origin or history of this apparent (mis-)meme?

      OT: Update: The other odd idiomatic usage I see that seems to be of biological origin is along the lines of:
          my $fh;
          unless (open $fh, ...) {
              print "open failed...";
              exit;
              }
      Huh?!?   Are BioMonks constitutionally averse to die-ing? Is it that  exit; is needed to return a non-error exit code to the OS?


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

        I had the same reaction. I would think that the ne, eq, etc. string relational operators all coerce their arguments into strings, so that no explicit stringification is needed. But I may miss something.

        Having said that, string comparison operators and arithmetic relational operator don't always return the same thing, so the OP should be aware of that. Remember that 10 is smaller than 3 for string relational operators.

      this assumes that neither $string_or_number nor any of the elements of @strings_or_numbers are ever undef

      It also makes other (corner case) assumptions. For example things get a bit dubious when the numbers (barewords) 1.4142135623730951, 1.4142135623730952 and 1.4142135623730953 (on a perl whose NV is double) are subjected to stringwise and numeric comparisons:
      use warnings; my $x = 1.4142135623730951; my $y = 1.4142135623730952; my $z = 1.4142135623730953; print "$x $y $z\n"; $x == $y ? print "x and y are numerically equal\n" : print "x and y are numerically unequal\n"; "$x" eq "$y" ? print "x and y are stringwise equal\n" : print "x and y are stringwise unequal\n"; $z == $y ? print "z and y are numerically equal\n" : print "z and y are numerically unequal\n"; "$z" eq "$y" ? print "z and y are stringwise equal\n" : print "z and y are stringwise unequal\n"; __END__ Outputs: 1.4142135623731 1.4142135623731 1.4142135623731 x and y are numerically equal x and y are stringwise equal z and y are numerically unequal z and y are stringwise equal
      So it makes the assumption that this behaviour is as wanted - which, if you look closely, is rather unlikely.
      Perhaps (not guaranteed) the first two results are as wanted - even though the two barewords are mathematically unequal they do convert to identical doubles and strings.

      But the last 2 results are just bizarre, imo.
      That perl should stringify two different NVs to the same string is rather pathetic - and it happens because perl by default stringifies doubles to only 14 decimal digits. (Update : make that 15, not 14 - the trailing zero for my example values was stripped.)
      If, like in python, the default was 17 decimal digits (as some believe it should be) then the discrepancy of the last 2 results would not arise.
      (We can, of course work around this stringification problem using sprintf, but that just makes it messy.)

      BTW, the same problem arises with perls whose NVs are 'long double' or '__float128'.
      Perl simply does not stringify floating point values to enough decimal digits.

      Cheers,
      Rob

      Since you’re testing for equality only, the straightforward solution is to stringify both terms in the comparison, thereby guaranteeing that you always compare a string with a string

      That doesn't work if one number is 0.5 and the other number is the string 0.50. (I mean, it'll show they're unequal when in fact they're equal.) But hopefully you don't have your numbers as strings like that.

      $_="msh210";$"=$\;@_=@{[split//,uc]}[2,0];$_="@_$\1";$\=$/;++$_[0]for$...1;print lc substr crypt($_,"@_"),1,6
Re: Filtering array of strings and numbers
by BillKSmith (Monsignor) on Apr 29, 2016 at 20:14 UTC
    It is impossible to do this perfectly! Consider two strings that consist of the same digits and different trailing whitespace. As numbers, they are equal. As strings, they are not. There is no way to tell (from the data alone) which is intended. Coercing everything to strings suppresses the "nastygrams" and is correct for all but a few special cases.
    Bill
      Say rather that it may take more effort than we would like (but it shouldn't be impossible) to fully describe what is meant by "perfectly" for a given task relating to a given set of data.
Re: Filtering array of strings and numbers
by dulwar (Monk) on Apr 29, 2016 at 19:50 UTC
    Hi nysus

    It's not clear from the question, but if you actually need to filter based on numerical equality, in the case where both the $string_or_number and the array element are numbers, then you can use the following:

    use Scalar::Util 'looks_like_number'; #... my @filtered = grep { defined && $string_or_number ne $_ && !(looks_like_number($_) && looks_like_number($string_or_number) && $string_or_number == $_) } @strings_or_numbers;

    That will, for example, filter out elements that are "5" when $string_or_number is "5.0".

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1161884]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chanting in the Monastery: (4)
As of 2024-04-19 14:50 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found