Как удалить повторяющиеся элементы из массива в Perl?
-
08-06-2019 - |
Вопрос
У меня есть массив в Perl:
my @my_array = ("one","two","three","two","three");
Как удалить дубликаты из массива?
Решение
Вы можете сделать что-то подобное, как показано в perlfaq4:
sub uniq {
my %seen;
grep !$seen{$_}++, @_;
}
my @array = qw(one two three two three);
my @filtered = uniq(@array);
print "@filtered\n";
Выходы:
one two three
Если вы хотите использовать модуль, попробуйте uniq
функция от List::MoreUtils
Другие советы
Документация Perl содержит хорошую коллекцию часто задаваемых вопросов.Ваш вопрос часто задают:
% perldoc -q duplicate
Ответ, скопированный и вставленный из вывода приведенной выше команды, отображается ниже:
Found in /usr/local/lib/perl5/5.10.0/pods/perlfaq4.pod How can I remove duplicate elements from a list or array? (contributed by brian d foy) Use a hash. When you think the words "unique" or "duplicated", think "hash keys". If you don't care about the order of the elements, you could just create the hash then extract the keys. It's not important how you create that hash: just that you use "keys" to get the unique elements. my %hash = map { $_, 1 } @array; # or a hash slice: @hash{ @array } = (); # or a foreach: $hash{$_} = 1 foreach ( @array ); my @unique = keys %hash; If you want to use a module, try the "uniq" function from "List::MoreUtils". In list context it returns the unique elements, preserving their order in the list. In scalar context, it returns the number of unique elements. use List::MoreUtils qw(uniq); my @unique = uniq( 1, 2, 3, 4, 4, 5, 6, 5, 7 ); # 1,2,3,4,5,6,7 my $unique = uniq( 1, 2, 3, 4, 4, 5, 6, 5, 7 ); # 7 You can also go through each element and skip the ones you've seen before. Use a hash to keep track. The first time the loop sees an element, that element has no key in %Seen. The "next" statement creates the key and immediately uses its value, which is "undef", so the loop continues to the "push" and increments the value for that key. The next time the loop sees that same element, its key exists in the hash and the value for that key is true (since it's not 0 or "undef"), so the next skips that iteration and the loop goes to the next element. my @unique = (); my %seen = (); foreach my $elem ( @array ) { next if $seen{ $elem }++; push @unique, $elem; } You can write this more briefly using a grep, which does the same thing. my %seen = (); my @unique = grep { ! $seen{ $_ }++ } @array;
Установить Список::MoreUtils из CPAN
Затем в вашем коде:
use strict;
use warnings;
use List::MoreUtils qw(uniq);
my @dup_list = qw(1 1 1 2 3 4 4);
my @uniq_list = uniq(@dup_list);
Мой обычный способ сделать это:
my %unique = ();
foreach my $item (@myarray)
{
$unique{$item} ++;
}
my @myuniquearray = keys %unique;
Если вы используете хэш и добавляете элементы в хэш.У вас также есть бонус: вы знаете, сколько раз каждый элемент появляется в списке.
Это можно сделать с помощью простого однострочного Perl.
my @in=qw(1 3 4 6 2 4 3 2 6 3 2 3 4 4 3 2 5 5 32 3); #Sample data
my @out=keys %{{ map{$_=>1}@in}}; # Perform PFM
print join ' ', sort{$a<=>$b} @out;# Print data back out sorted and in order.
Блок PFM делает это:
Данные из @in передаются в MAP.MAP создает анонимный хэш.Ключи извлекаются из хеша и передаются в @out.
Переменная @array — это список с повторяющимися элементами.
%seen=();
@unique = grep { ! $seen{$_} ++ } @array;
Последнее было довольно хорошо.Я бы немного подкорректировал:
my @arr;
my @uniqarr;
foreach my $var ( @arr ){
if ( ! grep( /$var/, @uniqarr ) ){
push( @uniqarr, $var );
}
}
Я думаю, что это, вероятно, наиболее читабельный способ сделать это.
Метод 1:Используйте хеш
Логика:Хэш может иметь только уникальные ключи, поэтому перебирайте массив, присваивайте любое значение каждому элементу массива, сохраняя элемент в качестве ключа этого хеша.Возвращайте ключи хеша, это ваш уникальный массив.
my @unique = keys {map {$_ => 1} @array};
Метод 2:Расширение метода 1 для возможности повторного использования
Лучше создать подпрограмму, если нам предстоит использовать эту функцию в нашем коде несколько раз.
sub get_unique {
my %seen;
grep !$seen{$_}++, @_;
}
my @unique = get_unique(@array);
Способ 3:Использовать модуль List::MoreUtils
use List::MoreUtils qw(uniq);
my @unique = uniq(@array);
Предыдущие ответы в значительной степени суммируют возможные способы решения этой задачи.
Однако я предлагаю модификацию для тех, кто не заботиться подсчет дубликаты, но делать заботиться о порядке.
my @record = qw( yeah I mean uh right right uh yeah so well right I maybe );
my %record;
print grep !$record{$_} && ++$record{$_}, @record;
Обратите внимание, что ранее предложенное grep !$seen{$_}++ ...
приращения $seen{$_}
перед отрицанием, поэтому приращение происходит независимо от того, было ли оно уже выполнено. %seen
или нет.Однако вышеизложенное приводит к короткому замыканию, когда $record{$_}
это правда, оставляя то, что было однажды услышано, «за пределами %record
'.
Вы также можете пойти на эту нелепость, которая использует преимущества автооживления и существования хеш-ключей:
...
grep !(exists $record{$_} || undef $record{$_}), @record;
Однако это может привести к некоторой путанице.
А если вас не волнует ни порядок, ни количество дубликатов, вы можете использовать еще один хак, используя срезы хеша и трюк, который я только что упомянул:
...
undef @record{@record};
keys %record; # your record, now probably scrambled but at least deduped
Попробуйте это, кажется, для правильной работы функции uniq нужен отсортированный список.
use strict;
# Helper function to remove duplicates in a list.
sub uniq {
my %seen;
grep !$seen{$_}++, @_;
}
my @teststrings = ("one", "two", "three", "one");
my @filtered = uniq @teststrings;
print "uniq: @filtered\n";
my @sorted = sort @teststrings;
print "sort: @sorted\n";
my @sortedfiltered = uniq sort @teststrings;
print "uniq sort : @sortedfiltered\n";
Использование концепции уникальных хэш-ключей:
my @array = ("a","b","c","b","a","d","c","a","d");
my %hash = map { $_ => 1 } @array;
my @unique = keys %hash;
print "@unique","\n";
Выход:а в б г