Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Translate one line of Perl to PHP

Tags:

php

perl

I am translating a file from Perl to PHP, but I need help with this line:
@stuff_unique = grep !$list{$_}++, @stuff;.
I know stuff_unique and stuff are arrays.

like image 421
Olsi Avatar asked Jan 10 '23 11:01

Olsi


2 Answers

This is a common Perl idiom, described in perlfaq4

With this construct, @stuff_unique will end up with a list of items that were seen at least once in @stuff; in other words, it is left holding only unique values, in the sense that there will be no repeats. The way it works is this:

A hash, in Perl, is like an associative array with unique keys. %list is such a hash. $list{something} is an element in that hash named 'something'. Its value can be whatever you place in it.

grep iterates over the items in @stuff. For each item in stuff, that item is used as a hash key in the %list hash. The ++ increments the value for that corresponding hash element. So if @stuff contained "1, 2, 1", then on the first iteration a hash element named "1" would be created. It has no value, which translates to Boolean false. The ! in front reverses the Boolean sense. So on that first iteration, the false value for the '1' hash element is evaluated as true, so that element passes through to @stuff_unique. Finally, post-increment takes place, so the value held in the 1 hash element increments to 1.

On the second element, the 2 has also not yet been seen, so it passes through, and its corresponding hash element is also incremented to 1.

On the third iteration, a '1' is seen again. $list{1} is already equal to 1, which is a true value. !true is false; so this one doesn't pass through to @stuff_unique.

One by one the elements in @stuff will be tested in this way; detecting if they've been seen before, and if they haven't, they pass through to @stuff_unique.

PHP provides a function called array_unique, which should do the same thing for you. It would be used like this:

$stuff_unique = array_unique($stuff);

Fortunately for the Perl people, this is a linear-time operation. Unfortunately for the PHP people, this is implemented internally by sorting the input array, and then iterating over it, skipping duplicates along the way. That means it's a O(n + n log n) operation (simplified to O(n log n)), which is to say, its implementation can't scale as well as the common Perl idiom.

like image 50
DavidO Avatar answered Jan 12 '23 00:01

DavidO


The joy of using php closures,

<?php

$stuff = array(1,1,2,2,2,3,3,3);

$list = array();
$stuff_unique = array_filter($stuff, function($_) use (&$list) {
  return !$list[$_]++;
});

print_r(array_values($stuff_unique));

or

<?php

$stuff = array(1,1,2,2,2,3,3,3);
$stuff_unique = array_keys(array_flip($stuff));

print_r($stuff_unique);

or

$stuff_unique = array_values(array_unique($stuff));
like image 36
mpapec Avatar answered Jan 12 '23 00:01

mpapec