So I have a few arrays with names that I want to search though, I would like to keep the arrays separate as they are each specific to a certain group of names. I'm trying to figure out how to search though more then one at the same time. The code I have below is how to search though one array but I'm not sure the best way to search multiple. I tried to add -and $array2 into the foreach but that did not work.
I know I could just add the same block for each array but I'm wondering if there is a cleaner and more efficient way to do that.
$array1 = "name1", "name2", "name3"
$array2 = "name4", "name5", "name6"
$searchname = Read-Host "Enter the name to search for"
foreach($name in $array1){
if($searchname -eq $name){
Write-Host "$searchname found"
}
}
If you just need to verify whether the name is present in any of the arrays you could simply concatenate them and check if the result contains the name you're looking for:
if (($array1 + $array2) -contains $name) {
Write-Host "$name found"
}
If you want to identify the array in which it was found you could do something like this:
'array1', 'array2' | ForEach-Object {
if ((Get-Variable $_).Value -contains $name) {
Write-Host "$name found in `$$_"
break
}
}
or like this, if the arrays were stored in a hashtable rather than individual variables:
$hash = #{
array1 = "name1", "name2", "name3"
array2 = "name4", "name5", "name6"
}
$hash.GetEnumerator() | ForEach-Object {
if ($_.Value -contains $name) {
Write-Host ('{0} found in ${1}' -f $name, $_.Name)
break
}
}
If you want to search across items in multiple arrays, you can concatenate the arrays in the foreach statement like so:
foreach($name in #($array1;$array2)){
if($searchname -eq $name){
Write-Host "$searchname found"
}
}
A more PowerShell-idiomatic approach would entail using the pipeline with the Where-Object filter cmdlet:
#($array1;$array2) |Where-Object {$_ -eq $searchname}
Use the PS3+ -in operator: $value -in $array
or the PS2+ -contains operator: $array -contains $value
In case of big arrays don't concatenate them as it's slow.
Organize the arrays in an array so you can enumerate them easier.
$arrays = #(
#("name1", "name2", "name3")
#("name4", "name5", "name6")
)
$searchname = Read-Host "Enter the name to search for"
1..$arrays.count | ForEach {
if ($searchname -in $arrays[$_-1]) {
Write-Host "$searchname found in array #$_"
}
}
Or use a hashtable:
$arrays = #{
foo = "name1", "name2", "name3"
bar = "name4", "name5", "name6"
}
$searchname = Read-Host "Enter the name to search for"
ForEach ($entry in $arrays.GetEnumerator()) {
if ($searchname -in $entry.value) {
Write-Host "$searchname found in array $($entry.key)"
}
}
Related
My JSON
[
{
"solution": "abc",
"solutionName": "abc_test",
"solutionShortcode": "",
"isManaged": false
},
{
"solution": "def",
"solutionName": "def_test",
"solutionShortcode": "def1",
"isManaged": true
}
]
What I need to do is take the solutionName and the solutionShortcode from each and insert them into a new hashtable - i thought this would work...
$buildDictionary = #{}
$b = Get-Content -Raw -Path ./temp.json | ConvertFrom-Json
foreach ($a in $b.solutionName.GetEnumerator()) {
$name = $b.solutionName.toString()
$shortcode = $b.solutionShortcode.toString()
if ([string]::IsNullOrEmpty($shortcode)) {
$shortcode = "0xxx"
}
$buildDictionary.Add("$name","$shortcode")
}
Write-Output $buildDictionary
Basically what I need to do is not that complex but there must be something I am missing about the ConvertFrom-Json cmdlet because this is not working as expected.
Essentially I need to add a generic value if the "shortcode" is empty and take insert the solutionName and solutionShortcode into the "buildDictionary" hashtable as a key/value pair.
What I have currently errors with "Key already added" error messages.. my resulting hashtable ends up with a single row consisting of a System.Object and not a key=value pair with string content.
Would appreciate some insight into what is wrong and why it is wrong.
Thanks!
...should do what you want:
$buildDictionary = #{}
$json = ConvertFrom-Json -InputObject (gc '.\temp.json' -raw)
$json | %{
If (!$_.solutionShortCode){
$_.solutionShortCode = 'someValue'
$buildDictionary.add($_.solutionName,$_.solutionShortcode)
}
}
If solutionShortCode is empty it sets 'someValue' as value and adds solutionName as Key to the HashTable $buildDictionary with the value solutionShortCode.
I have created 3 arrays, which store data from a separate file called household.dat, arr1 holds the ID, arr2 holds the income, and arr3 holds family members. The user is asked to enter an existent id (one that exists in the household file). If the id exists, I have to print the information related to that specific ID. The problem starts when I try to compare the user's input with the already stored id.
This is what I have so far.
$myarr contains the first set of numbers in the first column which is the ID
$myarr1 contains the second set of numbers in the second column, which is the income
$myarr2 contains the third set of numbers in the third column which is the members
10041,12180.00,4
15298,89254.00,3
10562,13240.00,3
13256,19800.00,2
47742,67189.00,4
14830,22458.00,8
19000,17000.00,2
21132,18125.00,7
23541,15623.00,2
82772,56878.00,2
32100,3200.00,6
67733,98113.00,5
36002,6500.00,5
37734,45144.00,4
65410,11970.00,2
47352,8900.00,3
62159,10000.00,2
92803,6200.00,1
"{0,25:n3}" -f "Household Statistics"
write-output "--------------------------------"
write-output "1. Search by Household ID"
write-output "2. List all"
$option = read-host "Enter a option (0 to quit)"
$myarr = #()
$myarr1 = #()
$myarr2 = #()
$id = #()
if($option -eq 1)
{
$id = Read-Host -Prompt "Enter Household ID "
foreach ($line in $file)
{
$line = $line -split (",")
$myarr = $line[0]
$myarr1 = $line[1]
$myarr2 = $line[2]
if ($myarr -contains $id)
{
write-output "--------------------------------"
write-host "Statistics for household" $id
write-output "--------------------------------"
"{0,-11} {1,10} {2,15}" -f "Household ID", "Income",
"Members"
}
elseif ($myarr -notcontains $id)
{
""
write-output "Sorry, entered Household ID not found"
break;
""
}
}
If you're not reading from a file as CSV, but somehow created 3 (same-length) arrays, you can start your code by combining the array values into workable objects like this:
$data = for ($i = 0; $i -lt $myarr.Count; $i++) {
[PsCustomObject]#{
ID = $myarr[$i]
Income = $myarr1[$i]
Members = $myarr2[$i]
}
}
However, reading your question a couple of times over, and looking at the examples you give for the 3 arrays, it seems to me that your file household.dat is in fact a CSV file without headers.
Instead of reading that file with Get-Content and then loop over the lines, manually splitting them on the comma as in your code, you should use Import-Csv, so you will obtain an array of objects where each object has both the 'ID', the 'Income' AND the corresponding 'Members' properties, nicely packed together.
In that case just do:
$data = Import-Csv -Path 'X:\somewhere\household.dat' -Header ID, Income, Members
Then your code would be much simpler like:
# loop over the records in the $data
$id = Read-Host -Prompt "Enter Household ID"
# see if you can find a matching record
$record = $data | Where-Object { $_.ID -eq $id }
if ($record) {
Write-Host "--------------------------------"
Write-Host "Statistics for household $id"
Write-Host "--------------------------------"
"{0,-11} {1,10} {2,15}" -f $record.ID, $record.Income, $record.Members
}
else {
""
Write-Host "Sorry, entered Household ID '$id' not found"
""
}
I have two arrays, $a and $b, within array $a is a string which could partially match one of the entries in $b, assuming I could use a wildcard:
$a = "1", "Computer Name", "2"
$b = "3", "4", "Full Computer Name Here"
foreach ($line in $a) {
foreach ($line2 in $b) {
where "*$line*" -like "*$line2*"
}
}
I've got here after trying all the simple 'this array matches that array', into foreach for one array, then tried all the Select-String to Compare-Object $line $line2 -ExcludeDifferent -IncludeEqual -PassThru, but can't get anything to work.
Ideally, it'd return 'Full Computer Name Here' where it matches.
Have you tried this?
$a = "1","Computer Name","2"
$b = "3","4","Full Computer Name Here"
foreach ($line in $a ) {
$b -match $line
}
EDIT:
Probably not the best answer despite it's simplicity as illustrated by #Ansgar in the comments. Sometimes PowerShell is so inconsistent it makes me wonder why I still use it.
Where-Object doesn't work that way. It reads from a pipeline that you don't have in your code. Also, your comparison is backwards, and you must not add wildcard characters to the reference value.
Change your code to something like this:
foreach ($line in $a) {
$b | Where-Object { $_ -like "*${line}*" }
}
or like this:
foreach ($line in $a) {
foreach ($line2 in $b) {
if ($line2 -like "*${line}*") { $line2 }
}
}
and it will do what you expect.
Edit:
I keep forgetting that comparison operators also work as enumerators, so the latter example could be simplified to something like this (removing the nested loop and conditional):
foreach ($line in $a) {
$b -like "*${line}*"
}
$b | Where {$_ | Select-String $a}
Updated 2018-06-23
Credits for LotsPings' comment to minimize it further to:
Apparently, Select-String has already both iterators in itself and therefore it can be simplified to just:
$b | Select-String $a
PS C:\> $a = "1", "Computer Name", "Other Name"
PS C:\> $b = "Computer", "4", "Full Computer Name Here", "something else", "Also full computer name here"
PS C:\> $b | Select-String $a
Full Computer Name Here
Also full computer name here
I'm having a simple issue with a script, where I want to run a GCI against a remote server, issue is, the value is combined with another hashtable property, so the GCI fails.
The script reads entries from a two-column .csv, the headers are "server" and "platform"
Here's what I've got:
$ShortDate = (Get-Date).ToString('MM/dd/yyyy')
$CheckServer = #{}
$serverObjects = #() # create a list of server objects
Import-Csv $Dir\Servers.csv | ForEach {
$CheckServer.Server = $_.Server
$CheckServer.Platform = $_.Platform
if (GCI \\$_.Server\c$\log\Completed_Summary_*.html -EA 0 | where {$.LastWriteTime -ge "$ShortDate"}) {
Write-Host "FOUND"
} # end of IF GCI
} # end of For-Each
$serverObjects += New-Object -TypeName PSObject -Property $CheckServer
The problem is that the entry for $_.Server should be SERVER1, SERVER2, SERVER3, etc, all the entries in the servers.csv, instead, the values for both $_.Server and $_.Platform are combined. Such as:
Write-Host "Checking" \\#{Server=SERVER1; Platform=PLATFORM_1}.Server\c$\log\Completed_Summary_*.html
it should show as follows:
Write-Host "Checking" \\SERVER1\log\Completed_Summary_*.html
How do I un-combine them so that the GCI command works?
PowerShell only does simple variable expansion inside strings. For more complex expressions like index operations or accessing object properties/methods it would insert the stringified value of the array or object variable and leave the rest of the operation untouched.
Demonstration:
PS C:\> $array = 23, 42
PS C:\> Write-Host "some $array[1] or other"
some 23 42[1] or other
PS C:\> $object = New-Object -Type PSObject -Property #{Foo=23; Bar=42}
PS C:\> Write-Host "some $object.Foo or other"
some #{Bar=42; Foo=23}.Foo or other
To avoid this you need to either:
assign the resulting value to a variable first and use that variable in the string:
$value = $array[5]
Write-Host "some $value or other"
$value = $object.Foo
Write-Host "some $value or other"
use a subexpression ($(...)):
Write-Host "some $($array[5]) or other"
Write-Host "some $($object.Foo) or other"
use the format operator (-f):
Write-Host "some {0} or other" -f $array[5]
Write-Host "some {0} or other" -f $object.Foo
modify like it
$ShortDate = Get-Date -Hour 0 -Minute 0 -Second 0
$CheckServer = #{}
$serverObjects = #() # create a list of server objects
$Dir="C:\temp"
Import-Csv $Dir\Servers.csv | ForEach {
$CheckServer.Server = $_.Server
$CheckServer.Platform = $_.Platform
if (GCI "\\$($_.Server)\c$\log\Completed_Summary_*.html" -EA 0 | where {$_.LastWriteTime -ge $ShortDate})
{
Write-Host "FOUND"
}
}
I am parsing a bunch of data in a textfile. I get the data with Get-Content.
Then I loop through each row in $data. Split each row on a space and load those values into an array.
I then loop through each $string in the array.
If the $string matches a specific value I want to delete it out of the array.
$index.Delete(), $index.Remove() does not work, Here is what I have.
$data = Get-Content "C:\Users\$userName\Desktop\test-data.txt"
foreach($row in $data){
if($row)
{
[Array]$index = $row.Split(" ")
$i = 0
foreach($string in $index){
Write-Host $string
if($string -eq "value1" -or $string -eq "value2" -or $string -eq "value3")
{
$index.Delete() //This does not work.
}
}
I have also tried something like this as well but it just was not working out at all.
for($i -eq $index.length; $i -le 0; $i++)
{
Write-Host $index[$i] #this would hit once then give me an error saying the value is null
if($index[$i] -eq "value1" -or $index[$i] -eq "value2" -or $index[$i] -eq "value3")
{
$index.Remove() #does not hit here at all/nor will it work.
Write-Host $index
}
}
How do I remove something from the $index array..?
Is there a better way to do this?
Any help would be much appreciated, thanks.
The easiest way would be to chain -ne operators:
[Array]$index = $row.Split(" ") -ne $value1 -ne $value2 -ne $value3
Each one will remove all the elements of the array that match the value in the variable, and the result will be passed on to the next. When it's finished, the array will contain the elements the didn't match any of the $value variables.
Try this:
[array]$index = $row.Split(" ",[stringSplitOptions]::RemoveEmptyEntries) -notmatch "\b(?:$value1|$value2|$value3)\b"