I cannot seem to find anything about using the values of one property of an object in a foreach loop (without having the entire object placed into the loop).
I first create a function called UFGet-Servers that uses Get-ADComputer and returns the names of the servers in a specific OU in my environment and places them in an array. That's great, except that when I use the array in a foreach loop, each object that it grabs has #[Name=serverName] in it, which I cannot use in any useful manner. The following pseudo-code is an abbreviated example:
foreach($Computer in $ComputerNames){do code... code is adding the server name into a UNC path such as "\\$Computer\C$\"}
The problem with the above is that you can't add the whole object to a path -- it ends up looking like "\#[Name=serverNameHere]\C$\" which totally bombs out. How do I get rid of the "#[property=" part, and simply use the value as the $Computer in the loop?
What really weirds me out is that I can't find a straightforward article on this anywhere... would have thought everyone and their mom would have wanted to do something like this.
So, your issue isn't with ForEach loops, it is with string formatting. There are two ways that I know of to take care of what you need. First is going to be string formatting, which allows you to use {0}m {1} and so on to inject values into a string, providing that you follow the string with -f and a list of said values. Such as:
ForEach($Computer in $ComputerNames){
"The Server Path is \\{0}\Share$" -f $Computer.Name
}
The second way is a sub-expression (I'm sure somebody will correct me if I used the wrong term there). This one involves enclosing the variable and desired property (or a function, or whatever) inside $(). This will evaluate whatever is inside the parenthesis before evaluating the string. See my example:
ForEach($Computer in $ComputerNames){
"The Server Path is \\$($Computer.name)\Share$"
}
Related
I've been following the guide on Exceljet for how to create a recursive LAMBDA function that removes all numbers from a string. For example, A1B2C3D4E5 becomes ABCDE.
The only thing I wanted differently was to have the string containing the character omissions stored inside the function rather than the cell. So, I took away the chars parameter from the opening line and used the LET function to store the string.
The problem is I keep getting a #VALUE! error and can't work out why.
=LAMBDA(str,sub,
LET(chars,"0123456789",
IF(chars="",str,
ReplaceChars(
SUBSTITUTE(str,LEFT(chars),sub),
MID(chars,2,LEN(chars)-1),
sub
)
)
))
A nested LET() in a recursive LAMBDA() is going to be troublesome since every time you'd use the variable in the recursion you'd start from scratch. Replacing characters will therefor never stop (if my interpretation of this is correct). But in your case you don't even need to make a recursive LAMBDA() to replace numbers, for example:
=LAMBDA(str,LET(X,MID(str,SEQUENCE(LEN(str)),1),CONCAT(IF(ISNUMBER(X*1),"",X))))
Core of this function is now:
=LET(X,MID(A1,SEQUENCE(LEN(A1)),1),CONCAT(IF(ISNUMBER(X*1),"",X)))
which can be used outside of LAMBDA() just fine. We can even replace this with
=LET(X,MID(A1,SEQUENCE(LEN(A1)),1),CONCAT(IF(ISNUMBER(FIND(X,"0123456789")),"",X)))
to do the same thing with any character you want to replace.
Now you can call =REPLACECHARS(A1). However, IMHO the beauty of the use of variables is now lost and the original approach (for example here would have my personal preference.
Possibly very stupid question I cannot seem to find an answer for (I am beginning with code)
I want to create a simple loop which appends myArray with three objects, which are members of a custom class MyClass. The objects have the following names: "object1", "object2", "object3".
When I write the following code, there is no issue:
myArray.append(object1)
But I want to write a loop to add all three. Again, very dumb, but I can't figure out how to insert the number in the name of the object as a variable. E.g., here was something I tried
let x = 3
for i in 1...x {
myArray.append(object[i])
}
This gives an error. The reason I want to do it using a loop, and not simply write in the three objects manually, is that I won't always loop three times. Sometimes I'll just want the first two objects, sometimes just the first.
I assume there's some easy way to do this, but when I search it tends to turn up more complex questions
I need to iterate and parse 108 lines from a file and then sort them into 3 different hashes. (In one iterator.) (In Ruby)
I have the file loaded into the program and into the array I need to parse. When I try to make the iterator anyway I try to use the Regex Match command I get an error abut the unknown method. Is as simple that I can't use that method on a array?
lines = File.readlines('access_log')
lines.each.match(/^([:\d\.]+) .*\[.*\].*\"[A-Z]+ *(.+) HTTP/)
This and every other way I have tried to use the match method it hasn't worked.
This also doesn't anything for the hash, as I haven't done that yet.
/^([:\d\.]+) .*\[.*\].*\"[A-Z]+ *(.+) HTTP/.match(lines)
Have also tried this, but the error output appears that you cant run it on only the array. I beilive this is where I would need to tie the iterator in, but I'm stumped.
So, what's happening is that what readlines does is it slurps the entire text file.
So you have an array with the content of the textfile separated by a newline(and the newline is kept in every string in the array).
After that, you're doing lines.each, which brings out an enumerator. Then you're calling .match on the enumerator instead of the string itself
The proper way to do this would be
lines.each { |line| line.match(/^([:\d\.]+) .*\[.*\].*\"[A-Z]+ *(.+) HTTP/) }
However, the above actually won't do anything because all you're doing is iterating against each element and checking if it matches the REGEX.If you want it to actually do something, try...
matches = lines.map { |line| line.match(/^([:\d\.]+) .*\[.*\].*\"[A-Z]+ *(.+) HTTP/) }
Remember that the match method only works on strings. If match matches something, it returns an object of the class MatchData, else if it doesn't match anything-- nil.
Test-OnlineFast is a (great) custom function which creates an array with the following output incredibly quickly:
I can use this output on its own, but if I want to list just the addresses using the commands:
$Pinged = Test-OnlineFast $MyIPList.ipaddress
$Pinged.Address
I receive the output:
Even though the array is a standard type:
This prevents me from doing things like comparing the array to another and matching the addresses.
Is there any way to 'convert' the array, so I can use it in this way? I've tried exporting it to a CSV or text file, and importing, but it's the same.
You could convert the array to a List<psobject>:
$list = [System.Linq.Enumerable]::ToList([psobject[]]$Array)
Since List<psobject> doesn't have a property called Address, you can now rely on property enumeration:
$list.Address
hash_test.pl
#a=("f","a","b");
$K{"f"}{"aa"}=1;
$K{"a"}{"aa"}=1;
$k{"b"}{"bb"}=1;
foreach(#a){
#c= sort keys %{$k{$_}};
}
print "#c\n";
foreach(#c) {...}
perl hash_test.pl
bb
I want to keep the keys of the hash into an array, so that I can use the array as an input for the following statements.
But it seemed that the assay #c just only hold the last element.
Could anyone tell me why or help me to improve the script?
You assign the array every time in the foreach, thus overwriting it every time. So you end up with only having the last thing assigned to it. If you move the print inside the foreach you'll see that they are all there.
To store those keys you need to add them to the array, not assign the array. I've corrected the typo $k to $K, and changed aa that goes with f to ff (expecting it to be a typo as well).
my #c;
foreach my $el (#a) {
push #c, sort keys %{$K{$el}};
}
print "#c\n";
This prints the line: ff aa bb. Every time through the loop all keys found in the hash for a particular array element are added to #c, each as a separate element. So #c will contain all bottom-level keys across the whole data structure.
However, there is more that I would like to suggest.
Always use strict; and use warnings; This is not pedantry but it directly helps. I never write code without them. The typo would be caught here, for example.
Use descriptive variable names. Specifically, single-letter variable names are just too easy to confuse, unless in very short loops or where it is crystal clear what they are. (For example, a typo like this couldn't really happen.) Most importantly, the code is going to be far nicer to work with. That generally results in better code.
Please use good indentation and spacing. It helps a lot, in many ways.
A useful core package for nested data structures is Data::Dumper, which can print the whole thing nicely formatted so we can see it. Try to add to the end of your code
use Data::Dumper;
print Dumper(\%K);
There are yet others that do the same or similar.
Here is another way to do what you ask.
my #lowest_keys = map { sort keys %{$K{$_}} } #a;
I call them lowest_keys to emphasize that these are the ones from the last hash in your data structure, the bottom of it. The map applies processing in the block { ... } to each element of #a in turn, returning a list with all these results. (If any one result itself is a list, with more elements than one, it gets merged into the overall output list. So this may create the output list with many more elements than the input.) This list can then be assigned to an array, as above, or passed on to another function that expects a list as input, or interated over.
The map is generally used to transform an array into another, by doing to each element what is in { ... } block. Its close cousin grep is used to filter, so passing through only the elements of the input list for which the condition in { ... } evaluates to true, forming the output list. For example, filter out undefined array elements: my #good = grep { defined } #all
Variable names are case sensitive, so %K... and %k... are not the same.
Always use
use strict;
use warnings;
and declare your variables with my. That prevents you from making this kind of mistakes.