Looping over associative array groups - bash

I have an associative array asc with its keys in the following form
asc[1-0]=dlc[0]
asc[2-1]=dlc[1]
asc[3-2]=dlc[2]
asc[1-3]=dlc[3]
asc[2-4]=dlc[4]
asc[3-5]=dlc[5]
asc[1-6]=dlc[6]
asc[2-7]=dlc[7]
asc[3-8]=dlc[8]
asc[1-9]=dlc[9]
asc[2-10]=dlc[10]
asc[3-11]=dlc[11]
asc[1-12]=dlc[12]
asc[2-13]=dlc[13]
...
I would like to group the elements by the first number when I call a function fn.
loop over i
fn asc[i-*] $ pass all elements with i as first number

You'll have to iterate over the keys of the associative array and build up an ordinary array of the values associated with the desired subset of keys.
for i in 1 2 3; do
group=()
for k in "${!asc[#]}"; do
[[ $k = $i-* ]] || continue
group+=("${asc[$k]}")
done
fn "${group[#]}"
done

Related

How do I get the unique values of a list in bash preserving order and keep the last value for each unique?

I have a list in bash that can have repeated values in it. I would like to remove duplicates and get a list with only the unique values in it. Order must be preserved and the last occurrence of the unique values is the one I wish to keep.
For example, if I have this list:
A=( D B A C D )
I'm looking for this:
result=( B A C D )
I've seen solutions for this when the data is a list in a file, but I'd prefer to keep the list in-memory without jumping through any hoops.
I think I can use an associative array and loop through the list adding the entries as keys in the array and then just dump the keys into the unique list but I'm not an expert with associative arrays across platforms -- do they sort themselves on key value sort of like a lot of C++ STL containers do or do they preserve the order of insertion regardless of key values?
I'd like to avoid a reliance on associative arrays though, because not all systems I may need to run on have bash 4.x or higher... some will be bash 3.x...
Any help would be great.
Without Associative Arrays
You can do it with indexed arrays by using an intermediate indexed array to hold unique values from A. This requires a nested loop over values stored in c[] for each element of A, e.g.
#!/bin/bash
declare -a result # declare result indexed array
declare -a c # declare temp intermediate indexed array
A=( D B A C D ) # original with duplicates
## loop decending over A, reset found flag, loop over c, if present continue,
# otherwise store A at index in c
for ((i = $((${#A[#]}-1)); i >= 0; i--)); do
found=0;
for j in ${c[#]}; do
[ "$j" = "${A[i]}" ] && { found=1; break; }
done
[ "$found" -eq '1' ] && continue
c[i]=${A[i]}
done
## loop over c testing if index for A exists, add from c to result
for ((i = 0; i < ${#A[#]}; i++)); do
[ "${c[i]}" ] && result+=(${c[i]})
done
declare -p result # output result
Example Use/Output
$ bash lastuniqindexed.sh
declare -a result='([0]="B" [1]="A" [2]="C" [3]="D")'
Using Associative Arrays with BASH_VERSION Test
You can do it with a combination of indexed and associative arrays making only a single pass though each array. You use an associative array B keyed with the value of A using B as a frequency array indicating whether an element of A has been seen. You then store the element of A in a temporary indexed array c[] so that the unique values can be added to result preserving the original order.
You can address whether associative array functionality is present with a bash version test at the beginning, e.g.
#!/bin/bash
case $BASH_VERSION in
## empty or beginning with 1, 2, 3
''|[123].*) echo "ERROR: Bash 4.0 needed" >&2
exit 1;;
esac
declare -A B # declare associative array
declare -a result # declare indexed array
A=( D B A C D ) # original with duplicates
## loop decending over A, if B[A] doesn't exist, set B[A]=1, store in c[]
for ((i = $((${#A[#]}-1)); i >= 0; i--)); do
[ -n "${B[${A[i]}]}" ] || { B[${A[i]}]=1; c[i]=${A[i]};}
done
## loop over c testing if index for A exists, add from c to result
for ((i = 0; i < ${#A[#]}; i++)); do
[ "${c[i]}" ] && result+=(${c[i]})
done
declare -p result # output result
Without the use of associative arrays, the nested loops looping over the original checking against each entry in c[] will be much less efficient as the size of the array grows.
Example Use/Output
$ bash lastuniq.sh
declare -a result='([0]="B" [1]="A" [2]="C" [3]="D")'
Look things over and let me know if you have further questions.

How to add 5 to every number of an array with Bash?

I am trying to create an array of user inputs and then add to each element in the array:
read number
for i in 1 2 3
read array[$i]
done
let position=0
for i in "${array[#]}"
do
let array[position]+=($i+$number)
let "position++"
done
for (( i=0; $i<3; i=$1+1 ))
do
echo ${array[$1]}
So, the user will enter "5" for number and then three more numbers for the array(90, 80, 70). The results should be array(95, 85, 75), but the output I'm getting is array(95, 175, 155).
A saner way to write this would be:
read -r number
read -r -a array
for idx in "${!array[#]}"; do
(( array[$idx] += number ))
done
printf '%s\n' "${array[#]}"
Instead of assuming that the indexes start at 0 (which they didn't, originally, because you were explicitly assigning to positions 1, 2 and 3), using "${!array[#]}" finds the actual indexes, and thus works correctly even with sparse arrays or ones not indexed starting at position 0.
Instead of duplicating $i (aka the values from your array) on the right-hand side of +=, it only adds the number itself.
Instead of iterating over indexes (again) to print the values, it just asks the array to dump all its values in index order with "${array[#]}".
See this in operation at https://ideone.com/WTLJSu
There is a behavioral difference insofar as it expects all the array values to be passed on a single line of input. If you don't want that, see the version at https://ideone.com/3OQtt3 instead.

Remove multiple elements from array based on index

I would like to remove multiple elements from an array based on their indexes.
array=("a" "b" "c" "d")
indexes=(1 3)
Output should be
array=("a" "c")
I know how to remove an element from an array knowing the index of the element:
If $i is the index:
array=("${(#)array[1,$i-1]}" "${(#)array[$i+1,$#array]}")
But what if I have multiple elements to remove? If I loop over the array of indexes, once I have removed one element the other indexes won't correspond anymore to the elements to be removed. So how is it possible to do that?
Using BASH arrays you can do this easily:
# original array
array=("a" "b" "c" "d")
# indexes array
indexes=(1 3)
# loop through indexes array and delete from element array
for i in "${indexes[#]}"; do
unset "array[$i]"
done
# check content of original array
declare -p array
declare -a array=([0]="a" [2]="c")
As per Chepner's comments below if OP wants an array of contiguous indices then loop through differential array and populate a new array
# result array
out=()
# loop through differential array and populate result
for i in "${array[#]}"; do
out+=("$i")
done
declare -p out
declare -a out=([0]="a" [1]="c")
Assuming indices is sorted, keep a counter of how many items you have already removed, and subtract that from each index in your loop.
count=0
for i in $indices; do
c=$((i - count))
array=("${(#)array[1,$c-1]}" "${(#)array[$c+1,$#array]}")
count=$((count + 1))
done
In bash, the same approach looks like
count=0
for i in "${indices[#]}"; do
c=$((i - count))
array=( "${array[#]:0:c-1}" "${array[#]:c+1}" )
count=$((count + 1))
done

Iterate through a PlatonScript array

I am developing on the top of the jPlaton platform and I want to declare a 10-element array of integers in PlatonScript.
Then, I want to assign a number to each array position, lets say numbers 1 to 10.
Finally, I want to iterate through this array and calculate and print the double of each array element.
Array
|1|2|3|4|5|6|7|8|9|10|
Output
|2|4|6|8|10|12|14|16|18|20|
How do I do those "for" loops?
Thanks
#ind:INTEGER
#arr:INTEGER[]
#ind=1
LOOP
setIndex arr #ind
IF (#ind>10)
BREAK
#arr = #ind
#ind=#ind+1
ENDLOOP
#dint:INTEGER
#dint=0
#ind=1
LOOP
setIndex arr #ind
IF (#ind>10)
BREAK
#dint=2*#arr
#ind=#ind+1
HTML
<p>#dint</p>
ENDHTML
ENDLOOP
Useful array methods
setIndex ObjectName IndexValue
Used in variable of array type
Set the current index of the variable
If index set to -1 then method returns the element count
of the array If index set to 0 then current index set to the last plus one (next empty). If index set to a number smaller or equal to the element count the currennt index is set to the specified number First position index is 1, next is 2 and so on. After we set the current array index we are able to access the current array element using the name of the variable as it was a simple (elementary) one
clear ObjectName
Clear the value of the variable. For arrays all the elements of the array are cleared and the array size is set to 0.
sort ObjectName
Sort the elements of an array type variable

Can't sort table with associative indexes

Why I can't use table.sort to sort tables with associative indexes?
In general, Lua tables are pure associative arrays. There is no "natural" order other than the as a side effect of the particular hash table implementation used in the Lua core. This makes sense because values of any Lua data type (other than nil) can be used as both keys and values; but only strings and numbers have any kind of sensible ordering, and then only between values of like type.
For example, what should the sorted order of this table be:
unsortable = {
answer=42,
true="Beauty",
[function() return 17 end] = function() return 42 end,
[math.pi] = "pi",
[ {} ] = {},
12, 11, 10, 9, 8
}
It has one string key, one boolean key, one function key, one non-integral key, one table key, and five integer keys. Should the function sort ahead of the string? How do you compare the string to a number? Where should the table sort? And what about userdata and thread values which don't happen to appear in this table?
By convention, values indexed by sequential integers beginning with 1 are commonly used as lists. Several functions and common idioms follow this convention, and table.sort is one example. Functions that operate over lists usually ignore any values stored at keys that are not part of the list. Again, table.sort is an example: it sorts only those elements that are stored at keys that are part of the list.
Another example is the # operator. For the above table, #unsortable is 5 because unsortable[5] ~= nil and unsortable[6] == nil. Notice that the value stored at the numeric index math.pi is not counted even though pi is between 3 and 4 because it is not an integer. Furthermore, none of the other non-integer keys are counted either. This means that a simple for loop can iterate over the entire list:
for i in 1,#unsortable do
print(i,unsortable[i])
end
Although that is often written as
for i,v in ipairs(unsortable) do
print(i,v)
end
In short, Lua tables are unordered collections of values, each indexed by a key; but there is a special convention for sequential integer keys beginning at 1.
Edit: For the special case of non-integral keys with a suitable partial ordering, there is a work-around involving a separate index table. The described content of tables keyed by string values is a suitable example for this trick.
First, collect the keys in a new table, in the form of a list. That is, make a table indexed by consecutive integers beginning at 1 with keys as values and sort that. Then, use that index to iterate over the original table in the desired order.
For example, here is foreachinorder(), which uses this technique to iterate over all values of a table, calling a function for each key/value pair, in an order determined by a comparison function.
function foreachinorder(t, f, cmp)
-- first extract a list of the keys from t
local keys = {}
for k,_ in pairs(t) do
keys[#keys+1] = k
end
-- sort the keys according to the function cmp. If cmp
-- is omitted, table.sort() defaults to the < operator
table.sort(keys,cmp)
-- finally, loop over the keys in sorted order, and operate
-- on elements of t
for _,k in ipairs(keys) do
f(k,t[k])
end
end
It constructs an index, sorts it with table.sort(), then loops over each element in the sorted index and calls the function f for each one. The function f is passed the key and value. The sort order is determined by an optional comparison function which is passed to table.sort. It is called with two elements to compare (the keys to the table t in this case) and must return true if the first is less than the second. If omitted, table.sort uses the built-in < operator.
For example, given the following table:
t1 = {
a = 1,
b = 2,
c = 3,
}
then foreachinorder(t1,print) prints:
a 1
b 2
c 3
and foreachinorder(t1,print,function(a,b) return a>b end) prints:
c 3
b 2
a 1
You can only sort tables with consecutive integer keys starting at 1, i.e., lists. If you have another table of key-value pairs, you can make a list of pairs and sort that:
function sortpairs(t, lt)
local u = { }
for k, v in pairs(t) do table.insert(u, { key = k, value = v }) end
table.sort(u, lt)
return u
end
Of course this is useful only if you provide a custom ordering (lt) which expects as arguments key/value pairs.
This issue is discussed at greater length in a related question about sorting Lua tables.
Because they don't have any order in the first place. It's like trying to sort a garbage bag full of bananas.

Resources