3

Coming from a C# background and being a complete PowerShell novice, I am trying to make a simple filtering based on two lists/arrays.

Goal: filter elements of list 'toFilter' based on list 'filter', so to only keep elements of 'toFilter', which are matching at least one of the patterns listed in 'filter'.

Ideally I would like to achieve that using piping or Linq (and not nested loops).

In C#, I would do it like this:

string[] filter  = {"A", "B", "C"};
string[] toFilter = {"AA", "AB", "DD"};

var filtered = toFilter.Where(s1 => filter.Any(s2 => s1.Contains(s2))).ToList();

or even shorther, using method groups:

var filtered = toFilter.Where(s1 => filter.Any(s1.Contains)).ToList();

With the expected outcome of:

AA AB

For Powershell, I found an article which describes powershell equivalents of Linq in C#, but apparently I cannot grasp the syntax.

I came with a following solution:

$filter = @("A", "B", "C")
$toFilter = @("AA", "AB", "DD")

$filtered = $toFilter | where {[Linq.Enumerable]::Any([string[]]$filter, [Func[string,bool]]{ $_ -match $args[0] })}

But it results in an empty list.

What would be a correct way of using those two arrays to come with an expected result?

EDIT: Apparently my solution works, but it is not pure LINQ, so a question is still valid.

3
  • i've seen that article. i feel really stupid reading it :) i wonder if someone could help Commented Jan 23, 2018 at 16:13
  • What version of PowerShell do you have? Your syntax looks flawed but actually generated the correct output for me on v5 Commented Jan 23, 2018 at 17:11
  • @Matt Funnily enough it has worked for me today, despite seeing empty output yesterday. I guess it might have something to do with re-assigning variables in the Windows Powershell interactive window. I am running v 5.1. Commented Jan 24, 2018 at 8:47

2 Answers 2

5

The reason your example fails is that $_ and $args[0] refer to the same thing in the inner delegate.

There's no need to employ LINQ here though - you can do the same with a nested Where-Object clause:

$filter = @("A", "B", "C")
$toFilter = @("AA", "AB", "DD")

$filtered = $toFilter | Where-Object {
    $item = $_
    @($filter |Where-Object {$item.Contains($_)}).Count -gt 0
}
Sign up to request clarification or add additional context in comments.

1 Comment

This is a very nice and readable solution, the only drawback I see is introducing an extra variable. BTW, I have run my solution today and strangely it works, so it seems that my reasoning was correct and '$args[0]' responds to elements passed in from 'filter' and the pipe variable '$_' is representing the values of 'toFilter' which is piped in - it does not change because of being inside of the delegate.
1

Cards on the table this is my first day working with LINQ. It seems like something I would like to be able to use. I tried to nest LINQ statements as best I could to try and get the right results using only LINQ.

[Linq.Enumerable]::where(
    $toFilter,
    [System.Func[System.Object,bool]]{ 
        param($filterItem)
        [Linq.Enumerable]::Any(
            $filter, 
            [Func[System.Object,bool]]{
                param($singlefilter) 
                # write-host "$filterItem -match $singlefilter"
                $filterItem -match $singlefilter}
        )
    }
)

Where to work over $toFilter and the Any checks each $filter against each $toFilter item.

2 Comments

This is pure LINQ-esque, nicely done. But seeing all the extra syntax I can see why LINQ in PowerShell is not so popular comparing to "normal" piping and where clauses.
100 percent agreed. However if you are looking for performance this still has a place

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.