Michael Vejar
Michael Vejar

Reputation: 11

How can I incorporate arrays to compare one value to all values in array?

I'm new to scripting and struggling with implementing some concepts. I've got a PowerShell script working that will take in input from the user and store it into several variables. It will then look at a .csv file and only output rows where the several inputs from the user is not like the value in a specified column.

For example, if I've got a .csv file that contains:

value1, value2, value3
data1, data2, data3
data1, data2, data3
data1, data5, data3
data1, data2, data3

And I ask to output the rows where value2 is not like data5 I get the following as output:

value1, value2, value3
data1, data2, data3
data1, data2, data3
data1, data2, data3

Here's the current code I have that is working:

$path = Read-Host 'What is the file share path?'

$filteredvalue = Read-Host 'What value would you like to filter out?'
$filteredvalue2 = Read-Host 'What second value would you like to filter out?'
$filteredvalue3 = Read-Host 'What second value would you like to filter out?'
$filteredvalue4 = Read-Host 'What second value would you like to filter out?'
$filteredvalue5 = Read-Host 'What second value would you like to filter out?'
$filteredvalue6 = Read-Host 'What second value would you like to filter out?'
$filteredvalue7 = Read-Host 'What second value would you like to filter out?'


$file1 = $path

$filtered = Import-Csv -Path $file1 | where {$_.IdentityReference -notlike $filteredvalue -and $_.IdentityReference -notlike $filteredvalue2 -and $_.IdentityReference -notlike $filteredvalue3 -and $_.IdentityReference -notlike $filteredvalue4 -and $_.IdentityReference -notlike $filteredvalue5 -and $_.IdentityReference -notlike $filteredvalue6 -and $_.IdentityReference -notlike $filteredvalue7}

$file2 = "${path}_filtered.csv"

$filtered | Export-Csv -Path $file2 -NoTypeInformation

This works but is not the best solution as I have to specify in the code how many values I'll be filtering out. I would like to use arrays to prevent having to create all the variables for the values.

Here's what I've tried so far but no luck:

$path = Read-Host 'What is the file share path?'

$Reponse = 'Y'
$filteredvalue = $Null
$filteredvalues = @()

Do 
{ 
    $filteredvalue = Read-Host 'What value would you like to filter out?'
    $Response = Read-Host 'Would you like to filter out additional values? (y/n)'
    $filteredvalues += $filteredvalue
}
Until ($Response -eq 'n')


$file1 = $path

$filtered = Import-Csv -Path $file1 | where {for($i = 0; $i -le $filteredvalues.GetUpperBound(0); $i++) {$_.IdentityReference -ne $filteredvalues[$i]}}


$file2 = "${path}_filtered.csv"

$filtered | Export-Csv -Path $file2 -NoTypeInformation

The output remains the same and nothing gets filtered out.

Upvotes: 1

Views: 115

Answers (1)

mklement0
mklement0

Reputation: 440317

PowerShell offers array-containment comparison operators -contains and, since v3, also -in - they differ only in whether the input array goes on the LHS (-contains) or RHS (-in).

As most operators in PowerShell, they also exist in negated form, i.e. -notcontains and -notin.

Applied to your code:

$filtered = Import-Csv $file1 | Where-Object IdentityReference -notin $filteredValues

As for what you tried:

As an aside: it's easier to use $filteredvalues.Count - 1 instead of $filteredvalues.GetUpperBound(0).

where (Where-Object) selects (passes through) input objects based on whether the output from the script block { ... } evaluates to $true.

Your for-loop code returns an array of Booleans, indicating for each filter value whether the input object's .IdentityReference matches that value.

Therefore, with 2 or more filter values, you'll end up with an array that contains at least 1 $true value, and any such array is invariably interpreted as $true overall.

For instance:

PS> [bool] ($false, $true)
True 

For an overview of how PowerShell implicitly evaluates object as Booleans (to-Boolean conversion), see the bottom half of this answer.

For your loop approach to have worked, you would have had to return $false as a scalar as soon as a filter value matched, and default to $true otherwise (note the use of foreach to enumerate the array elements more conveniently):

$filtered = Import-Csv -Path $file1 | where {
  foreach ($filteredValue in $filteredValues) {
    if ($_.IdentityReference -eq $filteredValue) { return $false }
  }
  return $true
}

Upvotes: 1

Related Questions