Bash - how to split an array element into a list - arrays

I have a file with a lot of filenames which are formatted like this:
filename1;filename2
filename3;filename4
filename5;filename6
I've managed to read this into an array for now, so I have 1 line per array element.
But I need to split every element into two elements, with the semicolon as a delimiter. So I would get something like an array in an array, where the inner one has always 2 elements?
As an endresult I'm trying to rename a bunch of files with this array.
How can I split this array, so I get this array within an array?

Split an array element into a list
IFS=\; read -ra list <<< "${yourArray[0]}" creates a list/array from your first array entry. But you cannot use that list as intended. Bash supports only 1D arrays. You cannot nest arrays.
Two arrays
Since your number of columns is constant and just 2, you could create one array for each column. This works well if you access col1 and col2 in a static manner only.
mapfile -t a yourFile
col1=("${a[#]/;*/}")
col2=("${a[#]/*;/}")
Fake a 2D array using an associative array
If you need dynamic access (e.g. the column is chosen by a variable) you could use eval or reference variables (declare -n), but the following hack could be better for small arrays and scrips where you access the array at a lot of different places dynamically:
mapfile -t a yourFile
declare -A field
for i in "${!a[#]}"; do
field["$i,1"]=${a[i]/;*/}
field["$i,2"]=${a[i]/*;/}
done
That way you can access the field in row $r and column $c using ${field[$r,$c]}.
For files with more than two columns use
mapfile -t a yourFile
declare -A field
for r in "${!a[#]}"; do
IFS=\; read -ra b <<< "${a[r]}"
for c in "${!cols[#]}"; do
field["$r,$c"]=${b[c]}
done
done

Related

Python:Compare two N multidimensional arrays and returns the difference

I have 2 json files, I am putting them into 2 multidimentional arrays in python, and I need to compare them and return an array of elements deleted, added and updated.
PS: I want to do this without taking in consideration that I know the depth of the arrays.

Maintain insertion order for a bash associative array

I'm scripting in Bash.
I have an issue with my associative array, when I put a record in my array like that:
declare -A arr_list_people_name
The way I put text in my associative array in a loop (put text sorted) :
arr_list_people_name[$peopleId]+=$peopleName
The way I read my array:
for KEY in "${!arr_list_people_name[#]}"; do
# Print the KEY value
echo "Key: $KEY"
# Print the VALUE attached to that KEY
echo "Value: ${arr_list_people_name[$KEY]}"
done
My list is not in the same order compare to the way I recorded it. However, I d like to find the same order than the way I recorded it in my array (sorted by value or key).
Do you have any idea how to manage that ?
You need to use a second, indexed array to store the keys in the order you add them to arr_list_people_name.
...
arr_list_people_name[$peopleId]+=$peopleName
arr_order+=("$peopleId")
...
for id in "${arr_order[#]}"; do
echo "Key: $id"
echo "Value: ${arr_list_people_name[$id]}"
done

Why does Powershell combines array of arrays?

I use arrays of arrays in a Powershell script, however, sometimes, my array of arrays, actually contains only one array.
For some reason, Powershell keeps replacing that array containing one array, by just one array. I don't get it, no other scripting / coding language I ever used has done that before.
Example, this is what I do not want:
PS C:\Users\> $a = #(#(123,456,789))
PS C:\Users\> $a[0]
123
This is what I want:
PS C:\Users\> $a = #(#(123,456,789), #())
PS C:\Users\> $a[0]
123
456
789
Why do I have to force an extra empty array for Powershell to consider my array of arrays as such when it only contains one array ? This is driving me nuts !
You need to put a comma as the first item:
$a = #(, #(123,456,789) )
The reason for this is that the comma is essentially the array construction parameters. This MSDN article has more information.
#() operator interpret its content as statements not as expression. Let us put explicit ;.
#(#(123,456,789;);)
What do you have here:
123,456,789 — binary comma operator create an array with three elements.
Result: array [123,456,789].
123,456,789; — as expression in this statement return collection, PowerShell enumerate this collection and write collection's elements (not collection itself) to the pipeline.
Result: three elements 123, 456 and 789 written to the pipeline.
#(123,456,789;) — array subexpression operator collect all the items written to pipeline as result of invocation of nested statements and create array from them.
Result: array [123,456,789].
#(123,456,789;); — as expression in this statement return collection, PowerShell enumerate this collection and write collection's elements (not collection itself) to the pipeline.
Result: three elements 123, 456 and 789 written to the pipeline.
#(#(123,456,789;);) — array subexpression operator collect all the items written to pipeline as result of invocation of nested statements and create array from them.
Result: array [123,456,789].
So, when you write #(collection), PowerShell return copy of collection, not collection wrapped into single element array. If you want to create array with single element, then you should use unary comma operator: ,expression. This will create single element array regardless of expression return collection or not.
Also, when you write #(a,b,c,etc), it is binary comma, who create array. Array subexpression operator just copy that array. But why do you need a copy? Is any reason, why you can not use original array? All you need for not making extra copy is just to omit # character: (a,b,c,etc).

Creating a multidimentional array from preexisting arrays in ruby

Firstly hello.
I am trying to create a basic calculator of sorts that transfers my array into a cvs file.
The problem that I came upon is that when I try to create a nested array in ruby with pre-existing arrays I get a 1D array where all of the values in each row are stuck someway
whole_array=[[part1],[part2],[part3]]
puts whole_array[1][1]
return part1[1]
When this is invoked it does not output anything with the puts command yet part1,part2,part3 all being arrays have a value
The only thing I can think of is that ruby forgets that those variables are and puts them in as strings

How do you read a csv file into a two dimensional array in BASH?

How do you read a csv file into a two dimensional array in BASH? The script needs to be dynamic enough where it can take csv files with variable number of rows and columns.
For example, if I have a csv file that looks like
AVERAGE STDEV MAX
17 18 19
or
AVERAGE STDEV MAX MIN
17 18 19 1
One way to simulate a two-dimensional array is to keep the rows as strings in a one-dimensional array and unpack them at each iteration. You will have to choose a suitable delimiter that doesn't appear in the data. Since you mention CSV, I'll use a comma, but this won't be smart enough to handle data like this with embedded commas:
name, start date, visits, games, balance
"Williamson, Dennis", "January 11, 2007", 12, 42, 17000
Here's a simple example of iterating over the values in a simulated two-dimensional array:
# avg, stddev, max, min
data_array=(
"17,18,19,1"
"12,14,16,2"
"6,8,10,3"
)
saveIFS=$IFS
for row in ${data_array[#]}
do
IFS=","
cols=($row)
IFS=$saveIFS
for col in ${cols[#]}
do
newval=$(do_something $col)
done
done
Making changes to the contents of the array is possible:
rowidx=2
colidx=2
IFS=","
cols=(${data_array[rowidx]})
cols[colidx]=$some_value
data_array[rowidx]="${cols[*]}"
IFS=$saveIFS
As you can see, it gets complicated fast and there are lots of gotchas which I haven't mentioned. Use Python.
bash supports only one-dimensional arrays. To see an emulation of 2 dimensions check out
twodim.sh in the advanced bash scripting guide:
example 27.17 in
http://tldp.org/LDP/abs/html/arrays.html
And I agree this does sound like homework.

Resources