Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to keep associative array order?

Tags:

arrays

bash

loops

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\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 

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?

like image 623
Doubidou Avatar asked Mar 20 '15 07:03

Doubidou


People also ask

Are associative arrays ordered?

So yes, they are always ordered. Arrays are implemented as a hash table.

Does PHP preserve array order?

PHP array is an ordered map, so, it's a map that keeps the order. array elements just keep the order since they were added that's all.

How do you sort an associative array in bash?

The best way to sort a bash associative array by KEY is to NOT sort it. Instead, get the list of KEYS, sort that list as a variable, and iterate through the list. Example: Suppose you have an array of IP addresses (keys) and host names (values):

How does an associative array work?

Associative arrays, also called maps or dictionaries, are an abstract data type that can hold data in (key, value) pairs. Associative arrays have two important properties. Every key can only appear once, just like every phone number can only appear once in a directory.


2 Answers

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?').

like image 84
Jonathan Leffler Avatar answered Sep 28 '22 10:09

Jonathan Leffler


My approach is to create a sorted array of keys first:

keys=( $( echo ${!dict[@]} | tr ' ' $'\n' | sort ) ) for k in ${keys[@]}; do     echo "$k=${dict[$k]}" done 
like image 43
Adi Degani Avatar answered Sep 28 '22 11:09

Adi Degani