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
Related
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
I have just this simple code of perl:
#!/usr/bin/perl
use Module::CoreList;
use local::lib;
#print ref( $Module::CoreList::version{5.014002});
#m = sort keys $Module::CoreList::version{5.014002};
So that I know some modules of particular version. But when tried to run:
Experimental keys on scalar is now forbidden at ./a line 5.
Type of arg 1 to keys must be hash or array (not hash element) at ./a line 5, near "};"
Execution of ./a aborted due to compilation errors.
But why does it take $Module::CoreList::version{5.014002} as scalar? When the type is hash (well array when you want to know keys of that hash)?
As per the documentation of Module::CoreList, %Module::CoreList::version returns a a hash of hashes that is keyed on perl version. So each element of the hash is a hash reference, not a hash.
You need to dereference the hash reference, by putting a % in front of it, as follows:
#m = sort keys %{ $Module::CoreList::version{5.014002} };
I am attempting to take a file and store the items from the file into an associative array in bash. An associative array might not be the best course for my action, but from my research it appears to be a good fit. I have a file that looks like the following:
personid
phone
phone
phone
personid
phone
phone
phone
Please note, persionid is always 20 digits, and phone is always 10. There is nothing else in this file, and no whitespace, just one of these per line. I wanted to map these to an associative array with my key being personid and my value being phone.
The code I've worked on regarding this piece is the following:
declare -A person
while read key; do
if [ ${#key} -eq 20 ] ; then
personID="$key"
else
phone="$key"
fi
person=( ["$personID"]="$phone" )
done < myFile.txt
for i in "${!person[#]}"
do
echo "key: $i"
echo "value: ${person[$i]}"
done
It will correctly store and print one personID as the key and one phone as the value....but there should be multiple values for phone. I'm thinking when I assign person, I must be doing that wrong, or have it in the wrong placed. But I've played around with it for quite sometime now and can't get it right. Never used associative arrays before, so having a little trouble. Thanks in advance for the help!
Associative arrays are no different to any other arrays in that there will always be exactly a 1:1 mapping from key (or index) to value. In your case, you want a 1:many mapping from key (personID) to values (phone numbers). One way to do this would simply to store a list of phone numbers which is effectively the "single" value mapped to a personID. You can construct your list however you like, with whitespace, or comma delimiters or anything else.
For example if you want : as your delimiter, you could do this to add each phone number to the list:
if [ -n ${person[$personID]} ] ; then
person[$personID]}="${person[$personID]}:$phone"
else
person[$personID]}="$phone"
fi
I have an associative array or Hash, is there a way where I can do manipulation on the Key of the Hash.
Forexample: Lets say I have a set of keys in the Keyset of Hash, I want to do some operations(Some string manipulation) on these keys.
Any suggestions on how to do this?
Use array names myarray to retrieve a list of all the keys, then you can do any string manipulation as you want on them
I'm not quite sure from your question whether you're talking about an array or a dictionary value; both are associative maps, but arrays are collections of variables and dictionaries are first-order values.
You can get the keys of an array with the array names command, and of a dictionary with dict keys:
# Note, access with*out* $varname
set keys [array names theArray]
# Note, access *with* $varname
set keys [dict keys $theDict]
In both cases, the keys variable will afterwards hold a normal Tcl list of strings that you can manipulate in any way you want. However, those changes do not reflect back to where they came from (as that isn't how Tcl's value semantics works, and would be really confusing in real code). To change the key of an entry in an array or dictionary, you have to remove the old one and insert the new one; this will (probably) change the iteration order.
set newKey [someProcessing $oldKey]
if {$newKey ne $oldKey} { # An important check...
set theArray($newKey) $theArray($oldKey)
unset theArray($oldKey)
}
set newKey [someProcessing $oldKey]
if {$newKey ne $oldKey} {
dict set theDict $newKey [dict get $theDict $oldKey]
dict unset theDict $oldKey
}
From Tcl 8.6.0 onwards you can also use dict map to do this sort of change with dictionaries:
set theDict [dict map {key value} $theDict {
if {[wantToChange $key]} {
set key [someProcessing $key]
}
# Tricky point: the last command in the sub-script needs to produce the
# value of the key-value mapping. We're not changing it so we use an empty
# mapping. This is one of the many ways to do that:
set value
}]
If you are changing a few keys in a large dictionary, it is more efficient to use dict set/dict unset as described earlier: dict map is optimized for the case where a lot of changes are being made.
Ok, I'm new to Perl but I thins this question is for Perl Gurus only :)
I need a well explain example of how to store and keep control of data read from a file.
I want to store them using an Associative array with index and then use a for loop to go over the array and print it to the screen.
For example:
my %array;
$array{$1} = [0]
foreach $array (sort values $array)
print "$value";
Something like this.
First, Perl refers to associative arrays as "hashes". Here's a simple example of reading the lines of a file and storing them in a hash to print them in reverse order. We use the line number $. of the file as the hash key and simply assign the line itself ($_) as the hash value.
#!/usr/bin/env perl
use strict;
use warnings;
my %hash_of_lines;
while (<>) {
chomp;
$hash_of_lines{$.} = $_;
}
for my $lineno ( sort { $b <=> $a } keys %hash_of_lines ) {
printf "%3d %s\n", $lineno, $hash_of_lines{$lineno};
}
Or even easier via the slurp method from IO::All:
#lines = io('file.txt')->slurp;
If you are reading a larger file you will probably want to lock the file to prevent racing conditions and IO::All makes it really easy to lock files while you are working with them.
You most likely do not want to use a hash (associative array is not a perl construct any longer) at all. What you are describing is using an array. Hashes are used for storing data connected with unique keys, arrays for serial data.
open my $fh, "<", $inputfile or die $!;
my #array = <$fh>;
print #array; # will preserve the order of the lines from the file
Of course, if you want the data sorted, you can do that with print sort #array.
Now, if that had been done with a hash, you'd do something like:
my %hash = map { $_ => 1 } <$fh>;
print sort keys %hash; # will not preserve order
And as you can see, the end result is only that you do not preserve the original order of the file, but instead have to sort it, or get a semi-random order. All the while, you're running the risk of overwriting keys, if you have identical lines. This is good for de-duping data, but not for true representation of file content.
You might think "But hey, what if I use another kind of key and store the line as the value?" Well, sure, you might take JRFerguson's advice and use a numerical index. But then you are using an array, just forsaking the natural benefits of using a proper array. You do not actually gain anything by doing this, only lose things.