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

I have a hash where its values are arrays. I pass it to a method as a hash ref. I want to add data as follows:
my ($k,$g,$a,$v,$f,$c) = split(/,/,$line); push(@{$href->{$v}},$a); sort(uniq(@{$href->{$v}}));
It didn't sort uniq the array and I didn't understand why. Finally I understand why - it does not make changes on the current array, rather creates a copy and does changes on that. So I have wrote the following code:
my ($k,$g,$a,$v,$f,$c) = split(/,/,$line); my @temp_arr; if(defined($href->{$v})) { @temp_arr = @{$href->{$v}}; } push (@temp_arr,$a); @{$href->{$v}} = sort(uniq(@temp_arr));
This code work as wanted, but the code does not look very good. Is it possible to achieve same logic with less lines or nicer solution? If it was possible, the first code would be great (if worked)

Replies are listed 'Best First'.
Re: Understanding how sort uniq works
by Eily (Monsignor) on Jul 25, 2019 at 14:43 UTC

    Like I said yesterday, if you don't care about the order in which the values were added (if you are going to sort the output anyway, for example), you can store the $version as hash keys, rather than an array. (This is the rough equivalent of Python's sets, like what you showed us in the CB yesterday). Your structure can by using this statement instead of the push:

    $hash->{$value}{$version}++; # The names you used yesterday
    . And then you can iterate over it with:
    for my $outer (keys %$hash) { my $subhash = $hash->{$outer}; for my $inner (sort { $a <=> $b } keys %$subhash) { # iterating over the sorted values } }
    The values are stored unsorted, but you can just sort them at the last moment when going through them. You can use Data::Dump (or Data::Peek as pointed by Tux :) ). To see what the content of the $hash looks like.

    Edit: try to avoid one-letter names, but never use $a outside of sort, as it has a special meaning for that case and some others (like some functions of List::Util)

Re: Understanding how sort uniq works
by roboticus (Chancellor) on Jul 25, 2019 at 14:50 UTC

    ovedpo15:

    Perhaps you might try:

    my ($k, $g, $a, $v, $f, $c) = split /,/, $line; if (defined $href->{$v}) { $href->{$v} = [ sort uniq @{$href->{$v}} ]; }

    I've not tested it, but the intent is to get rid of @temp_array. To do so, I put @{$href->{$v}} after uniq to pass in the contents of the array since I'm guessing that uniq isn't expecting an array reference. I've also wrapped the sort in [ and ] to put the returned array back into $href->{$v} as an array reference.

    Update: On re-reading your question, I think I'd try this instead:

    my ($k, $g, $a, $v, $f, $c) = split /,/, $line; $href->{$v} = [ sort uniq $a, @{$href->{$v}} ];

    Again, it's untested.

    ...roboticus

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

Re: Understanding how sort uniq works
by BillKSmith (Monsignor) on Jul 26, 2019 at 15:52 UTC
    I assume that you want the first example to produce the same result as the second. You can do this by storing the result of the sort the same way.
    use strict; use warnings; use List::Util qw(uniq); use Data::Dumper; my $line = 'k,g,a,v,f,c'; my $href = { v => ['foo'], }; my ($k,$g,$a,$v,$f,$c) = split(/,/,$line); push(@{$href->{v}},$a); @{$href->{v}} = sort(uniq(@{$href->{v}})); # Compare to your Case II print Dumper($href);
    Bill
Re: Understanding how sort uniq works
by hippo (Archbishop) on Jul 26, 2019 at 09:06 UTC
    Is it possible to achieve same logic with less lines or nicer solution?

    Nicer is in the eye of the beholder. However, here's an SSCCE:

    use strict; use warnings; use Test::More tests => 2; use List::MoreUtils 'uniq'; my $v = 'ovedpo15'; my $href = { $v => [5, 3, 2] }; pushsortuniq ($href->{$v}, 3); is_deeply ($href->{$v}, [2, 3, 5], 'Duplicate added, sorted'); pushsortuniq ($href->{$v}, 4); is_deeply ($href->{$v}, [2, 3, 4, 5], 'Non-duplicate added, sorted'); sub pushsortuniq { my ($aref, @topush) = @_; push @$aref, @topush; @$aref = sort (uniq(@$aref)); }

      Why not just

      sub sortuniq { my $aref = shift @_; @$aref = sort(uniq(@$aref, @_)); }
      c:\@Work\Perl\monks>perl -wMstrict -le "use Test::More tests => 2; use List::MoreUtils 'uniq'; ;; my $v = 'ovedpo15'; my $href = { $v => [5, 3, 2] }; ;; sortuniq ($href->{$v}, 3); is_deeply ($href->{$v}, [2, 3, 5], 'Duplicate added, sorted'); sortuniq ($href->{$v}, 4); is_deeply ($href->{$v}, [2, 3, 4, 5], 'Non-duplicate added, sorted'); ;; sub sortuniq { my $aref = shift @_; @$aref = sort(uniq(@$aref, @_)); } " 1..2 ok 1 - Duplicate added, sorted ok 2 - Non-duplicate added, sorted

      Update: You can shorten that further to
          sub sortuniq { @{$_[0]} = sort(uniq(@{$_[0]}, @_[ 1 .. $#_ ])); }
      but that just makes things a bit more messy IMHO.


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

Re: Understanding how sort uniq works
by ikegami (Patriarch) on Jul 26, 2019 at 03:30 UTC

    It didn't sort uniq the array and I didn't understand why.

    It sorted the unique values of the array. You simply didn't save the sorted values anywhere.