Как сохранить порядок ассоциативного массива?
Я пытаюсь итерироваться по ассоциативному массиву в Bash.
Сначала это кажется простым, но цикл не сохраняет исходный порядок элементов массива.
Вот простой скрипт для тестирования:
#!/bin/bash
echo -e "Рабочее окружение\n----------";
lsb_release -a
echo -e "\nВерсия Bash\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 "Результат\n----------";
for i in "${!groups[@]}"
do
echo "$i => ${groups[$i]}";
done
Выходные данные:
Рабочее окружение
----------
Нет доступных модулей LSB.
Идентификатор дистрибутива: Ubuntu
Описание: Ubuntu 14.04.2 LTS
Версия: 14.04
Кодовое имя: trusty
Версия Bash
----------
4.3.11(1)-release.
Результат
----------
group3 => 789
group2 => 456
group1 => 123
group5 => def
group4 => abc
Почему я не получаю group1
, group2
и т.д. в порядке их объявления?
Я не хочу, чтобы порядок был алфавитным, мне просто нужно, чтобы цикл следовал тому порядку, в котором я объявил элементы массива...
Существует ли способ это сделать?
3 ответ(ов)
Ваш подход выглядит вполне разумным. Вы создаете отсортированный массив ключей в словаре, а затем проходите по этому массиву и выводите каждый ключ со значением. Вот пример кода, который может вам помочь:
keys=( $( echo ${!dict[@]} | tr ' ' $'\n' | sort ) )
for k in ${keys[@]}; do
echo "$k=${dict[$k]}"
done
Здесь вы используете команду echo ${!dict[@]}
для получения всех ключей из ассоциативного массива dict
, затем преобразуете их в строки с помощью tr
, и сортируете с помощью команды sort
. Это позволяет вам получить отсортированный список ключей. В цикле for
вы просто выводите ключи и соответствующие им значения.
Обратите внимание, что если у вас большой словарь, то регулярное использование команд вроде echo
и sort
может быть не самым эффективным с точки зрения производительности, но для небольших массивов такой способ вполне приемлем.
Еще один способ сортировки элементов в ассоциативном массиве — это сохранять список групп по мере добавления их в массив. Назовите этот элемент с ключом "group_list". Каждый раз, добавляя новую группу, добавляйте её в поле group_list, вставляя пробел для разделения последующих добавлений. Вот пример, который я использовал для своего ассоциативного массива, названного master_array:
master_array["group_list"]+="${new_group} ";
Чтобы пройтись по группам в порядке их добавления, вы можете перебрать поле group_list в for цикле и затем получить доступ к полям групп в ассоциативном массиве. Вот пример кода, который я написал для 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"
Это похоже на предложение Джонатана Леффлера, но данные хранятся вместе с ассоциативным массивом, не требуется поддерживать два отдельных несвязанных массива. Как видите, порядок не случайный и не алфавитный, а именно тот, в котором я добавлял элементы в массив.
Кроме того, если у вас есть подгруппы, вы можете создать списки подгрупп для каждой группы и пройтись по ним. Именно поэтому я осуществил это так, чтобы избежать необходимости в множественных массивах для доступа к ассоциативному массиву и одновременно позволить расширять группы без необходимости модифицировать код.
В приведенном коде представлены несколько удобных абстракций на Bash, которые позволяют работать с ассоциативными массивами и сохранять порядок добавления элементов. Вот разбор функций:
Функция
add()
: Эта функция используется для добавления парключ-значение
в ассоциативный массив и одновременного сохранения порядка вставки. Она принимает три аргумента: имя переменной, ключ и значение. Внутри функции создаются глобальные ассоциативный массив и массив для порядка:add() { local var=$1 local key=$2 local val=$3 declare -ga "${var}_ORDER" # Массив для хранения порядка declare -gA "${var}_MAP" # Ассоциативный массив для хранения значений local -n map=${var}_MAP # Является ссылкой на ассоциативный массив local -n order=${var}_ORDER # Является ссылкой на массив порядка order+=("$key") # Добавляем ключ в массив порядка map["$key"]=$val # Сохраняем значение по ключу }
Функция
get()
: Эта функция возвращает значение по заданному ключу из ассоциативного массива.get() { local var=$1 local key=$2 local -n tmp=${var}_MAP # Используем ссылку на ассоциативный массив echo "${tmp[$key]}" # Возвращаем значение по ключу }
Функция
keys()
: Функция выводит все ключи, которые были добавлены в порядок.keys() { local var=$1 local -n tmp=${var}_ORDER echo "${tmp[@]}" # Выводим все ключи }
Функция
values()
: Возвращает все значения в порядке их добавления.values() { local var=$1 local -n map=${var}_MAP local -n order=${var}_ORDER for k in "${order[@]}"; do echo -n "${map[$k]} " # Выводим значение по каждому ключу done echo }
Цикл для вывода всех пар
ключ => значение
:for i in $(keys groups); do echo "$i => $(get groups "$i")" # Выводим ключ и соответствующее ему значение done
Таким образом, этот набор функций позволяет удобно управлять данными в структурированном виде, сохраняя при этом их порядок. Вы можете добавлять, получать, и перечислять как ключи, так и значения, что делает код более удобочитаемым и поддерживаемым.
Цикл по массиву в JavaScript
Добавить новый элемент в массив в Bash без указания индекса
Как разделить строку на массив в Bash?
Как объединить элементы массива Bash в строку с разделителем?
Как извлечь определенный элемент из массива в BASH?