Как сохранить порядок ассоциативных массивов?
Я пытаюсь перебрать ассоциативный массив в Bash.
Это кажется простым, но цикл не соответствует начальному порядку массива.
Вот простой скрипт:
#!/bin/bash
echo -e "Workspace\n----------";
lsb_release -a
echo -e "\nBash version\n----------";
echo -e $BASH_VERSION."\n";
declare -A groups;
groups["group1"]="123";
groups["group2"]="456";
groups["group3"]="789";
groups["group4"]="abc";
groups["group5"]="def";
echo -e "Result\n----------";
for i in "${!groups[@]}"
do
echo "$i => ${groups[$i]}";
done
Вывод:
Workspace
----------
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 14.04.2 LTS
Release: 14.04
Codename: trusty
Bash version
----------
4.3.11(1)-release.
Result
----------
group3 => 789
group2 => 456
group1 => 123
group5 => def
group4 => abc
Почему у меня нет group1
, group2
и т.д.?
Я не хочу иметь алфавитный порядок, я просто хочу, чтобы цикл следовал начальному порядку объявления массива...
Есть ли способ?
Ответы
Ответ 1
Как уже указывалось, ошибки нет. Ассоциативные массивы хранятся в порядке "хэш". Если вы хотите заказать, вы не используете ассоциативные массивы. Или вы используете неассоциативный массив, а также ассоциативный массив.
Сохраните второй (неассоциативный) массив, который идентифицирует ключи в том порядке, в котором они созданы. Затем выполните второй массив, используя его содержимое для ввода первого (ассоциативного) массива при печати данных. Вот так:
declare -A groups; declare -a orders;
groups["group1"]="123"; orders+=( "group1" )
groups["group2"]="456"; orders+=( "group2" )
groups["group3"]="789"; orders+=( "group3" )
groups["group4"]="abc"; orders+=( "group4" )
groups["group5"]="def"; orders+=( "group5" )
# Convoluted option 1
for i in "${!orders[@]}"
do
echo "${orders[$i]}: ${groups[${orders[$i]}]}"
done
echo
# Convoluted option 1 - 'explained'
for i in "${!orders[@]}"
do
echo "$i: ${orders[$i]}: ${groups[${orders[$i]}]}"
done
echo
# Simpler option 2 - thanks, PesaThe
for i in "${orders[@]}"
do
echo "$i: ${groups[$i]}"
done
"Более простой вариант 2" был предложен PesaThe в comment и следует использовать в предпочтении "запутанной опции".
Пример вывода:
group1: 123
group2: 456
group3: 789
group4: abc
group5: def
0: group1: 123
1: group2: 456
2: group3: 789
3: group4: abc
4: group5: def
group1: 123
group2: 456
group3: 789
group4: abc
group5: def
Вероятно, вы не хотите иметь два оператора в строке, но он подчеркивает parallelism между обработкой двух массивов.
Точки с запятой после заданий в вопросе на самом деле не нужны (хотя они не оказывают никакого активного вреда, за исключением того, что читатель задается вопросом "почему?" ).
Ответ 2
Другим способом сортировки записей в вашем ассоциативном массиве является сохранение списка групп по мере добавления их в качестве записи в ассоциативном массиве. Вызовите этот ключ ввода "group_list". Когда вы добавляете каждую новую группу, добавьте ее в поле group_list, добавив пустое пространство для разделения последующих дополнений. Здесь я сделал для ассоциативного массива, который я назвал master_array:
master_array["group_list"]+="${new_group}";
Чтобы выполнить последовательность через группы в том порядке, в котором вы их добавили, выполните последовательность из поля group_list в цикле для, затем вы можете получить доступ к полям группы в ассоциативном массиве. Здесь фрагмент кода для одного, который я написал для master_array:
for group in ${master_array["group_list"]}; do
echo "${group}";
echo "${master_array[${group},destination_directory]}";
done
и здесь вывод из этого кода:
"linux"
"${HOME}/Backup/home4"
"data"
"${HOME}/Backup/home4/data"
"pictures"
"${HOME}/Backup/home4/pictures"
"pictures-archive"
"${HOME}/Backup/home4/pictures-archive"
"music"
"${HOME}/Backup/home4/music"
Это похоже на предложение Джонатана Леффлера, но хранит данные с ассоциативным массивом, а не требует сохранения двух отдельных непересекающихся массивов. Как вы можете видеть, это не в произвольном порядке, ни в алфавитном порядке, а в порядке, в котором я добавил их в массив.
Кроме того, если у вас есть подгруппы, вы можете создавать списки подгрупп для каждой группы, а также следовать через их. Это причина, по которой я сделал это таким образом, чтобы облегчить доступ к множеству массивов для доступа к ассоциативному массиву, а также разрешить расширение для новых подгрупп без необходимости изменять код.
EDIT: исправлено несколько опечаток
Ответ 3
Мой подход заключается в том, чтобы сначала создать отсортированный массив ключей:
keys=( $( echo ${!dict[@]} | tr ' ' $'\n' | sort ) )
for k in ${keys[@]}; do
echo "$k=${dict[$k]}"
done