Удалить массив записей из другого массива
У меня есть 2 массива
a=(1,2,3,4,5)
b=(2,4)
выход должен быть
c=(1,3,5)
(который должен быть результатом ab)
Я пытался использовать
unset a[${b}]
есть идеи?
то, что я работаю сейчас, это цикл, который проходит через 700000 итераций
5 ответов
Прежде всего, если вы хотите сделать это за 700000 итераций, вам действительно следует заняться чем-то другим, кроме bash. Кроме того, то, что вы показываете, это не массив в bash, а строка1. Массивы разделяются пробелами, а не запятыми.
Тем не менее, вот способ bash, предполагающий истинные массивы:
a=(1 2 3 4 5)
b=(2 4)
c=( $(printf "%s\n" "${a[@]}" "${b[@]}" | sort | uniq -u) )
Если у вас есть строки, разделенные запятыми, а не массивы, используйте вместо этого:
a=(1,2,3,4,5)
b=(2,4)
c=( $(sed 's/,/\n/g' <(printf "%s\n" "${a[@]}" "${b[@]}") | sort | uniq -u) )
В качестве альтернативы вы можете использовать вместо этого Perl:
#!/usr/bin/perl
my @A=(1,2,3,4,5);
my @B=(2,4);
my %k;
map{$k{$_}++} (@A,@B);
my @C=grep($k{$_}==1,keys(%k));
print "@C\n";
1Строго говоря, это массив с одним элементом, но по сути это то же самое, что и строка в отношении bash.
perl
путь. Этот скрипт отображает все элементы в массиве a
, которые также содержатся в массиве b
,
#!/usr/bin/perl
my @a = (1,2,3,4,5);
my @b = (2,4,7,8,9,10);
# Create a hashmap with the entries in b as keys,
# but without values for the keys for a better lookup
# (exists ($hash{$element}))
my %hash;
@hash{@b}=();
foreach my $element (@a) {print "$element " unless exists($hash{$element})}
print "\n";
Выход:
1 3 5
Сложность задачи (O(N^2)) не может быть уменьшена; предполагая только то, что было дано явно (т.е. нет гипотезы для значений обоих массивов a
а также b
) код ниже просто проверит, присутствует ли каждое значение в a
также присутствует в b
и, если значение присутствует, оно сломает внутренний for
цикл (единственная возможная оптимизация с данной гипотезой) и добавить значение к c
; с большим количеством подсказок о содержимом массивов a
а также b
(т. е. если значения в каждом массиве могут быть повторены и если массивы отсортированы), может быть еще больше возможностей для улучшения:
#!/bin/bash
a=(1 2 3 4 5)
b=(2 4)
for i in ${a[@]}
do
match=0
for j in ${b[@]}
do
if [ "${i}" == "${j}" ]
then
match=1
break
fi
done
if [ "${match}" == 0 ]
then
c+=($i)
fi
done
echo ${c[@]}
В python
, записи, такие как:
a=(1,2,3,4,5)
b=(2,4)
являются повторяемыми и известны как tuples
,
Ваша задача может быть легко выполнена в python
:
#!/usr/bin/env python2
a = (1, 2, 3, 4, 5)
b = (2, 4)
c = tuple(i for i in a if i not in b)
print c
Выход:
(1, 3, 5)
Здесь мы нашли значения кортежа a
, которые не существуют в кортеже b
и положить их в другой кортеж c
, Также обратите внимание, что эта операция будет быстрой и потребляющей память для больших наборов данных, так как мы использовали выражение генератора Python.
Принятый ответ @terdon на самом деле не удаляет элементы из тех, которые также находятся в , а скорее объединяет оба списка и удаляет неуникальные значения. Это огромная разница, когда включают элементы, которых нет в : будут содержать значения, которые есть в , но не в .
Вот чистое решение Bash для фактического удаления элементов, которые также находятся в (обратите внимание на дополнительный in):
a=(1 2 3 4 5)
b=(2 4 6)
c=( $(printf "%s\n" "${a[@]}" "${b[@]}" "${b[@]}" | sort | uniq -u) )
будет состоять из
1 3 5
и исключить неожиданное
6
.
Обратите внимание: предполагается, что все значения уникальны. В противном случае они не будут отображаться в . Если содержит повторяющиеся значения, вы должны сначала удалить дубликаты (обратите внимание на повторяющиеся значения).
1
в ):
a=(1 1 2 3 4 5)
b=(2 4 6)
c=( $({ printf "%s\n" "${a[@]}" | sort -u; printf "%s\n" "${b[@]}" "${b[@]}"; } | sort | uniq -u) )
Если вы хотите реплицировать дубликаты в
c
, используйте ответ @kos. То же самое верно, если
a
а также
b
огромны: это очень неэффективное решение для многих элементов, даже если оно незначительно для нескольких элементов (<100). Если вам нужно обрабатывать огромные массивы, не используйте Bash.