Output $Error using a variable in the array index - arrays

Have this code, which takes the errors and output the exceptions.
I want to use a variable in the index of an array, but it does not resolve.
[int32]$ErrorCounter = $Error.Count
$ErrorResponse = $($Error[($ErrorCounter)].Exception)
Write-Host "$A, $B, $ErrorResponse"
This appears in a loop, and the $error contains information I want to parse. So it needs to output the latest $error entry, not the whole $error array.

As Ansgar noted, the most recent error is at index 0. If you want the oldest error message, which is what your original message does, you would need to subtract one from $ErrorCounter since the index is 0 based. Or just use the built-in powershell accessor like $ErrorResponse = $Error[-1].Exception. The -1 starts from the end of the collection instead of the beginning. You can use a negative number up to the size of your $count you computed earlier.

Related

Data Rows to String to Array issue

I have a very weird problem happening. I have an object populated with a bunch of rows. I can access them all well, but I need to append a "." (dot) to every value inside it, so I end up converting each record to a string using a for each loop and adding a "." after trimming the values. The issue now however, is that I would like to assign each of these rows (And the rows have only one column/Item) to another array/object, so I can access them later.
The issue is that even when I declare an object/array variable and assign the string converted data rows, the object variable keeps getting converted to a String, and I just cant seem to avoid it.
Please help. Here is a modified code sample:
[String] $DBNms = #();
ForEach($DBName in $objDBNms){
$tempText += $DBName.Item(0).ToString().Trim() + "."
$DBNms += $tempText
}
Write-Host($DBNms.GetType()) #And this is showing up a string, while I want it to be an array.
And if I print $DBNms, it indeed shows up a string concatenated together into a single string, while I actually want it to be like $DBNms[0] = first Item value, $DBNms[1] = second Item value and so on.
[String] $DBNms = #(); makes $DBNms a type-constrained variable, due to type literal [string] being placed to the left of variable $DBNms, whose type is then locked in as a string; that is, as a single string.
You were looking to create a string array, which in PowerShell is represented as [string[]]:
[string[]] $DBNames = #()
PowerShell's type conversions are automatic (and very flexible), so that [String] $DBNms = #() doesn't report an error, it quietly converts the empty array (#()) to the empty string (as required by the type constraint):
PS> [string] $DBNames = #(); '' -eq $DBNames
True
A much more efficient way to collect values from multiple iterations in an array is to use the foreach statement as an expression, which case PowerShell collects the outputs automatically for you:
[string[]] $DBNms = foreach ($DBName in $objDBNms){
# Output this expression's value as-is.
# PowerShell will collect the individual iterations' values for you.
$DBName.Item(0).ToString().Trim() + "."
}
The above is not only more concise than your original approach, it is more importantly more efficient:
In reality you cannot directly add (append to) an array, because it is an immutable data structure.
What PowerShell has to do whenever you use += with an array is to allocate a new array behind the scenes, with the original elements copied over and the new element(s) appended; the new array is then automatically assigned back to the variable.
Note: The alternative and next best solution to using the whole loop as an expression is to use an efficiently extensible list type, notably [System.Collections.Generic.List[object]](System.Collections.Generic.List`1):
# Create the list.
# The 'System.' namespace prefix is optional.
[Collections.Generic.List[string]] $DBNms = #()
foreach ($DBName in $objDBNms) {
# Add to the list, using its .Add() method.
# Note: Do NOT try to add with +=
$DBNms.Add($DBName.Item(0).ToString().Trim() + ".")
}
You'll need this approach if your loop does more than outputting a single value per iteration, such as needing to append to multiple collections.
Note: It used to be common to use System.Collections.ArrayList instances instead; however:
Use of this type is no longer recommended (see the warning in the linked help topic); use [System.Collections.Generic.List[object]] instead, which additionally allows you to strongly type the collection (replace [object] with the type of interest, such as [string] in the code above).
It has the drawback of its .Add() method having a return value, which you need to silence explicitly (e.g., $null = $lst.Add(...)), so that it doesn't accidentally pollute your code's output stream (produce unexpected extra output).

How do I return a string as a one element array reference in Perl?

I'm new to Perl, and this thing has got me stuck for far too long...
I want to dump a readable representation of the object itself from inside a function (I'm trying to debug something, and I'm doing this by returning an array reference which the caller expects, but containing an object dump rather than human readable text as per normal) so in my package I have:
use Data::Dumper;
sub somefunctionName{
my $self = shift;
my $d = Dumper($self);
my #retval = ();
push(#retval, $d);
return \#retval;
}
This is giving me the error "Can't use string ("the literal object dump goes here") as a HASH ref while "strict refs" in use"
I can't for the life of me figure out a way to make the error go away, no matter how I mess with the backslashes, and what I've done above looks to me like exactly what every online tutorial does... But I'm obviously missing the point somewhere.
What am I doing wrong?
According to the documentation
Dumper(LIST)
Returns the stringified form of the values in the list, subject to the configuration options below. The values will be named $VAR n in the output, where n is a numeric suffix. Will return a list of strings in a list context.
You should be able to do
#retval = Dumper($self);
return \#retval

can't convert Enumerator into Array

While working on one application I am getting this error:
can't convert Enumerator into Array
Here is my code, mr_collection is MongoID query.
mr_collection = self.where(query).map_reduce(map, reduce).finalize(finalize).out({:replace => 'mr_results'})
paginator = WillPaginate::Collection.new(page, limit, collection_count)
collection = mr_collection.find(
:sort => sort,
:limit => limit,
:skip => skip
)
paginator.replace(collection)
While getting mr_collection, if I inspect the result mr_collection gives me:
[
{"_id"=>1.0, "value"=>{"s"=>4.2, "p"=>14.95, "pml"=>0.01993}},
{"_id"=>2.0, "value"=>{"s"=>3.7, "p"=>12.9, "pml"=>0.0172}},
{"_id"=>3.0, "value"=>{"s"=>4.2, "p"=>12.9, "pml"=>0.0172}},
{"_id"=>4.0, "value"=>{"s"=>4.0, "p"=>11.95, "pml"=>0.01593}},
{"_id"=>300.0, "value"=>{"s"=>0.0, "p"=>8.95, "pml"=>0.01193}},
]
While getting collection, if I inspect the result collection gives me:
#<Enumerator: []:find({:sort=>[["value.s", :desc], ["value.pml", :asc]], :limit=>10, :skip=>0})>
I am getting error on the line paginator.replace(collection). I'm using Ruby 1.9.3 & Rails 3.2.6.
collection is an Enumerator which obviously can't convert into an Array, which is what replace expects.
Here are the comments from the rubydocs:
Enumerable#find(ifnone = nil) { |e| ... }
Passes each entry in enum to block. Returns the first for which block
is not false. If no object matches, calls ifnone and returns its
result when it is specified, or returns nil otherwise.
If no block is given, an enumerator is returned instead.
Therefore you have two options:
If you want all elements, yield from the Enumerator to an Array.
If you only want the first match, supply a block that determines what the match is.
Hope this helps.
(Moral of the story: always read the docs!)
I have no idea about mongoid having never used it.
But a search has brought to fore an awfully similar question -
Mongoid 3 - access map_reduce results
Unfortunately my environent is not set to test the magic of
collection = mr_collection.send(:documents).sort(sort).limit(limit).skip(skip).to_a
Have you had a look at this link? Hopefully it'll help solve your issue!

can't read : variable isn't array

I have the following code :
set arr1(a1) t1
set arr2(a2) t2
set l1 {}
lappend l1 arr1
lappend l1 arr2
set arr3(a3) $l1
foreach names [array names arr3] {
set value $arr3($names)
puts "names = $names, value = $value"
foreach ar $value {
if {[array exists $ar]} {
puts "$ar is an array"
foreach {key val} [array get $ar] {
set d1 $ar($key)
puts "ar key = $key value = $val "
}
}
}
}
but when I run the tcl script it fails for the line "set d1 $ar($key)" . The error msg is 'can't read "ar(a1)": variable isn't array' . Can you please suggest what is causing the error and how do I resolve the same.
When you use the syntax $ar($key), you are looking up the key $key in the array ar and returning its value. This is how Tcl is defined to work, it's in the basic language syntax. However, you're using the ar variable to hold a scalar value, not an array (the two are completely separate; arrays aren't values, though lists and dictionaries are). That's why you're getting the error message.
To read from an array that is named in a variable, you either need to use a longer piece of syntax so that you substitute the variable name and then read from that variable (Tcl doesn't do this for you by default, since it's quite dangerous if you're not prepared for it) or you need to make an alias to the named array variable.
Double-Substitution via set
set d1 [set ${ar}($key)]
This works because $… is really (under the hood) an alias for set with a single argument. (Well, except that it doesn't actually call the command; they both call the same C API.) We use the ${...} form to limit what the initial $ uses as its variable name. Be aware that if you put an array element name in ar, you'll get odd results from this.
Aliasing an Array or an Element
upvar 0 $ar theAlias
set d1 $theAlias($key)
The upvar command links variables together, and in particular when used with 0 as its first argument, it aliases variables in the current scope. By establishing theAlias as a fixed alias to the actual array (the one named by $ar), we can then access it just like a normal array. You could also alias directly to an element:
upvar 0 ${ar}($key) theAlias
set d1 $theAlias
Note the same syntax as used with the set solution above; we want the name of the element, not to read it. (Warning: do not alias to elements of the global env array; the code that couples to the system environment variables does not work in a friendly way with aliases.)
The main issue with using upvar is that you can't turn theAlias back into a non-aliased variable (though you can retarget the alias by calling upvar again) other than by throwing away the current stack frame (trivial for a procedure body, not too hard for a namespace via namespace delete, but problematic with the global namespace as deleting that terminates the whole Tcl interpreter).

Perl -- DBI selectall_arrayref when querying getting Not Hash Reference

I am very new to perl (but from a c# background) and I am trying to move some scripts to a windows box.
Due to some modules not working easily with windows I have changed the way it connects to the DB.
I have an sqlserver DB and I had a loop reading each row in a table, and then within this loop another query was sent to select different info.
I was the error where two statements can't be executed at once within the same connection.
As my connection object is global I couldn't see an easy way round this, so decided to store the first set of data in an array using:
my $query = shift;
my $aryref = $dbh->selectall_arrayref($query) || die "Could not select to array\n";
return($aryref);
(this is in a module file that is called)
I then do a foreach loop (where #$s_study is the $aryref returned above)
foreach my $r_study ( #$s_study ) {
~~~
my $surveyId=$r_study->{surveyid}; <-------error this line
~~~~
};
When I run this I get an error "Not a hash reference". I don't understand?!
Can anyone help!
Bex
You need to provide the { Slice => {} } parameter to selectall_arrayref if you want each row to be stored as a hash:
my $aryref = $dbh->selectall_arrayref($query, { Slice => {} });
By default, it returns a reference to an array containing a reference to an array for each row of data fetched.
$r_study->{surveyid} is a hashref
$r_study->[0] is an arrayref
this is your error.
You should use the second one
If you have a problem with a method, then a good first step is to read the documentation for that method. Here's a link to the documentation for selectall_arrayref. It says:
This utility method combines
"prepare", "execute" and
"fetchall_arrayref" into a single
call. It returns a reference to an
array containing a reference to an
array (or hash, see below) for each
row of data fetched.
So the default behaviour is to return a reference to an array which contains an array reference for each row. That explains your error. You're getting an array reference and you're trying to treat it as a hash reference. I'm not sure that the error could be much clearer.
There is, however, that interesting bit where it says "or hash, see below". Reading on, we find:
You may often want to fetch an array
of rows where each row is stored as a
hash. That can be done simple using:
my $emps = $dbh->selectall_arrayref(
"SELECT ename FROM emp ORDER BY ename",
{ Slice => {} }
);
foreach my $emp ( #$emps ) {
print "Employee: $emp->{ename}\n";
}
So you have two options. Either switch your code to use an array ref rather than a hash ref. Or add the "{ Slice => {} }" option to the call, which will return a hash ref.
The documentation is clear. It's well worth reading it.
When you encounter something like "Not a hash reference" or "Not an array reference" or similar you can always take Data::Dumper to just dump out your variable and you will quickly see what data you are dealing with: arrays of arrayrefs, hashes of something etc.
And concerning reading the data, this { Slice => {} } is most valuable addition.

Resources