Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
741 views
in Technique[技术] by (71.8m points)

bash - How to keep associative array order?

I try to iterate over an associative array in Bash.

It seems to be simple, but the loop doesn't follow the initial order of the array.

Here is a simple script to try:

#!/bin/bash

echo -e "Workspace
----------";
lsb_release -a

echo -e "
Bash version
----------";
echo -e $BASH_VERSION."
";

declare -A groups;
groups["group1"]="123";
groups["group2"]="456";
groups["group3"]="789";
groups["group4"]="abc";
groups["group5"]="def";

echo -e "Result
----------";
for i in "${!groups[@]}"
do
    echo "$i => ${groups[$i]}";
done

The output:

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

Why I don't have group1, group2, etc.?

I don't want to have an alphanum order, I just want that the loop follow the initial declaration's order of the array...

Is there a way?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

As already pointed out, there is no mistake. Associative arrays are stored in a 'hash' order. If you want ordering, you don't use associative arrays. Or, you use a non-associative array as well as an associative array.

Keep a second (non-associative) array that identifies the keys in the order that they're created. Then step through the second array, using its contents to key the first (associative) array when printing the data. Like this:

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

The 'simpler option 2' was suggested by PesaThe in a comment, and should be used in preference to the 'convoluted option'.

Sample output:

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

You probably don't want to have two statements per line like that, but it emphasizes the parallelism between the handling of the two arrays.

The semicolons after the assignments in the question are not really necessary (though they do no active harm, beyond leaving the reader wondering 'why?').


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...