Proper use of Where-Object

Hello,

I’m having trouble using the Where-Object to filter some output.
I’m creating an object for some json data, and although I can list key properties, I can’t seem to filter my results to get “whatever” key(s) I want.

Here’s my process flow:

$object = Get-Content -Path D:\Outfile_MP4.json | ConvertFrom-Json # create object from json file
$object | Get-Member # reveals two NoteProperty objects: 'format' and 'streams'

#sample output
format      NoteProperty System.Management.Automation.PSCustomObject format=@{filename=D:\Movies\12 Angry Men (1957).mp4; 
streams     NoteProperty Object[] streams=System.Object[]

Focusing on the format object:

$object.format | Select-Object -Property * | Format-List # outputs 'keys' and 'values' in list

filename         : D:\Movies\12 Angry Men (1957).mp4
nb_streams       : 3
nb_programs      : 0
format_name      : mov,mp4,m4a,3gp,3g2,mj2
format_long_name : QuickTime / MOV
start_time       : 0.000000
duration         : 5769.930833
size             : 10848568174
bit_rate         : 15041522
probe_score      : 100
tags             : @{major_brand=mp42; minor_version=512; compatible_brands=mp42iso6; creation_time=2022-03-04 05:09:52; title=12 Angry Men (1957); comment=Henry Fonda, Lee J. Cobb, Ed Begley   
                   and Jack Klugman lead in this tense, courtroom drama - nominated for three Oscars including Best Picture - about one juror determined to sway the opinions of eleven others.}
$object.format | Select-Object -Property * | Get-Member

   TypeName: Selected.System.Management.Automation.PSCustomObject

Name             MemberType   Definition
----             ----------   ----------
Equals           Method       bool Equals(System.Object obj)
GetHashCode      Method       int GetHashCode()
GetType          Method       type GetType()
ToString         Method       string ToString()
bit_rate         NoteProperty string bit_rate=15041522
duration         NoteProperty string duration=5769.930833
filename         NoteProperty string filename=D:\Movies\12 Angry Men (1957).mp4
format_long_name NoteProperty string format_long_name=QuickTime / MOV
format_name      NoteProperty string format_name=mov,mp4,m4a,3gp,3g2,mj2
nb_programs      NoteProperty long nb_programs=0
nb_streams       NoteProperty long nb_streams=3
probe_score      NoteProperty long probe_score=100
size             NoteProperty string size=10848568174
start_time       NoteProperty string start_time=0.000000
tags             NoteProperty System.Management.Automation.PSCustomObject tags=@{major_brand=mp42; minor_version=512; compatible_brands=mp42iso6; creation_time=2022-03-04 05:09:52; title=12 An…
($object.format | Get-Member -Name *).Name # outputs keys only

Equals
GetHashCode
GetType
ToString
bit_rate
duration
filename
format_long_name
format_name
nb_programs
nb_streams
probe_score
size
start_time
tags

My attempt at trying to filter the output from either of the above outputs:

$object.format | Select-Object -Property | Where-Object -Property Equals 'filename'
$object.format | Select-Object -Property * | Where-Object -Property Equals "filename"
$object.format | Select-Object -Property * | Where-Object -Property Equals "*name*"

POSH just outputs an error that I’m doing it all wrong… :slight_smile:

I’m trying to figure out how I can search for a specified key(s) and…

  • output those ‘filtered’ keys in a list
  • output those ‘filtered’ keys and their respective values in a list.

Thanks for any help or guidance with this…

A sample input file might have been helpful. :wink:

What is what you’re actually trying to do? How would you expect your output to look like?

If you want to show only certain properties you can use Select-Object and specify the ones you’re after:

$object | 
    Select-Object -Property filename, format_name, format_long_name, duration, size

If you’re looking for a particular object with a specific value in one (or more) property you use Where-Object

$object |
    Where-Object -Property 'filename' -Match -Value 'Angry'

And BTW: A " ...| Select-Object -Property * " does not make that much sense when you pipe the output to the next cmdlet anyway. :wink:

I was worried about the amount of space it would take up. I didn’t see an option to upload anything other than an image file, so I’ll just include the ‘format’ object in json format considering this is the section I’m working with for now. Once I can work with this small set of data, I’ll switch to the ‘streams’ object which is much larger.

],
    "format": {
        "filename": "D:\\Movies\\12 Angry Men (1957).mp4",
        "nb_streams": 3,
        "nb_programs": 0,
        "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
        "format_long_name": "QuickTime / MOV",
        "start_time": "0.000000",
        "duration": "5769.930833",
        "size": "10848568174",
        "bit_rate": "15041522",
        "probe_score": 100,
        "tags": {
            "major_brand": "mp42",
            "minor_version": "512",
            "compatible_brands": "mp42iso6",
            "creation_time": "2022-03-04T05:09:52.000000Z",
            "title": "12 Angry Men (1957)",
            "comment": "Henry Fonda, Lee J. Cobb, Ed Begley and Jack Klugman lead in this tense, courtroom drama - nominated for three Oscars including Best Picture - about one juror determined to sway the opinions of eleven others."
        }
    }
}

Ultimately, I was hoping to use this opportunity to learn POSH better when it comes to retrieving and manipulating data. I thought I could use Get-Member to provide me with enough information to understand the structure of the objects to include those 2nd level objects/arrays.

I started with the following goal in mind:

  1. List all keys without values
  2. List all keys with their respective values
  3. List specified keys without values
  4. List specified keys with their respective values
  5. Find specific value(s) regardless of key

#1 - List all keys without values
Only achieved using Get-Member, but that doesn’t appear to be the best approach.

($object.format | Get-Member -Name *).Name

#2 - List all keys with their respective values
Able to achieve this using the following, although I’m not sure why I used Format-List in my earlier example. :slight_smile:

$object.format | Select-Object -Property *

#3 - List specified keys without values
This one was a total failure; similar to goal #1.

#4 - List specified keys with their respective values
Using your Select-Object example outputs the specified keys and their respective values.

$object.format | Select-Object -Property filename, format_name, format_long_name, duration, size

Considering the custom objects structure from json data, I will likely need to loop through all sub-arrays? The json ‘format’ has one sub-array called ‘tags’.

#5 - Find specific values regardless of key
This one is I’m not having any luck with either.

Testing your Where-Object, I was hoping to return the ‘filename’ key with the ‘value’ only.

$object.format | Where-Object -Property 'filename' -Match -Value 'Angry'

Unfortunately, it’s spitting out all of the key-value pairs. The same output I get with:

$object.format | Select-Object -Property *

I tried using wildcards "*angry*", but same output as before.

Perhaps this is returning the ‘format’ object as it contains the key named ‘filename’ which contains the ‘angry’ value, instead of just the ‘filename’ key (property) that contains the ‘angry’ value?
I was thinking that because I’m looking within the $object.format object, that it should only be looking at the keys…?

True that… I was literally just throwing cr*p at the wall at this point to see what sticks… :slight_smile:

In the meantime I’ll continue brushing up on the proper use of the Where-Object for retrieving data from a custom object.

Thanks again Olaf…

If you want to post sample data you simply post them as plain text formatted as code. This way we can easily copy these data and play with it to be able to reproduce the issues you might have. And of course you don’t have to post all of them - a few sample data sets would be enough.

The sample data you posted seemed broken or incomplete. I created some working sample data from it. So we can use it to help you.

$JSONInput = @'
[
    {
        "format": {
            "filename": "D:\\Movies\\12 Angry Men (1957).mp4",
            "nb_streams": 3,
            "nb_programs": 0,
            "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
            "format_long_name": "QuickTime / MOV",
            "start_time": "0.000000",
            "duration": "5769.930833",
            "size": "10848568174",
            "bit_rate": "15041522",
            "probe_score": 100,
            "tags": {
                "major_brand": "mp42",
                "minor_version": "512",
                "compatible_brands": "mp42iso6",
                "creation_time": "2022-03-04T05:09:52.000000Z",
                "title": "12 Angry Men (1957)",
                "comment": "Henry Fonda, Lee J. Cobb, Ed Begley and Jack Klugman lead in this tense, courtroom drama - nominated for three Oscars including Best Picture - about one juror determined to sway the opinions of eleven others."
            }
        }
    },
    {
        "format": {
            "filename": "D:\\Movies\\13 Relaxed And Nice Men (1958).mp4",
            "nb_streams": 3,
            "nb_programs": 0,
            "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
            "format_long_name": "QuickTime / MOV",
            "start_time": "0.000000",
            "duration": "3869.930833",
            "size": "10988568174",
            "bit_rate": "15041522",
            "probe_score": 100,
            "tags": {
                "major_brand": "mp42",
                "minor_version": "512",
                "compatible_brands": "mp42iso6",
                "creation_time": "2023-03-04T05:09:52.000000Z",
                "title": "13 Relaxed And Nice Men (1958)",
                "comment": "Nice family entertainment"
            }
        }
    }
]
'@ | 
ConvertFrom-Json

Now you can output the contained list simply by calling the variable:

$JSONInput

The output looks like this:

PS C:\_Sample> $JSONInput

format
------
@{filename=D:\Movies\12 Angry Men (1957).mp4; nb_streams=3; nb_programs=0; format_name=mov,mp4,m4a,3gp,3g2,mj2; format_long_name=QuickTime / MOV; start_time=0.000000; duration=5769.930833; si… 
@{filename=D:\Movies\13 Relaxed And Nice Men (1958).mp4; nb_streams=3; nb_programs=0; format_name=mov,mp4,m4a,3gp,3g2,mj2; format_long_name=QuickTime / MOV; start_time=0.000000; duration=3869… 

When you want to output only the key names from the format node you can use a trick. Every PowerShell object has a hidden property PSObject. You can use this to access things like the names of the headers of CSV data for example. Or the names of the keys of your JSON data node:

$JSONInput.format[0].PSObject.Properties.Name

The output looks like this

PS C:\_Sample> $JSONInput.format[0].PSObject.Properties.Name
filename
nb_streams
nb_programs
format_name
format_long_name
start_time
duration
size
bit_rate
probe_score
tags

If you’re looking for a specific node you can use a Where-Object filter like this:

$JSONInput.format |
    Where-Object -Property 'filename' -Match -Value 'Angry'

And as expected the output looks like this:

PS C:\_Sample> $JSONInput.format |
>>     Where-Object -Property 'filename' -Match -Value 'Angry'

filename         : D:\Movies\12 Angry Men (1957).mp4
nb_streams       : 3
nb_programs      : 0
format_name      : mov,mp4,m4a,3gp,3g2,mj2
format_long_name : QuickTime / MOV
start_time       : 0.000000
duration         : 5769.930833
size             : 10848568174
bit_rate         : 15041522
probe_score      : 100
tags             : @{major_brand=mp42; minor_version=512; compatible_brands=mp42iso6; creation_time=04.03.2022 05:09:52; title=12 Angry Men (1957); comment=Henry Fonda, Lee J. Cobb, Ed Begley  
                   and Jack Klugman lead in this tense, courtroom drama - nominated for three Oscars including Best Picture - about one juror determined to sway the opinions of eleven others.} 

Of course you can limit the output to some properties of your choice:

$JSONInput.format |
    Select-Object -Property filename, format_long_name, @{Name = 'Runtime'; Expression = {'{0} min' -f [Math]::Round($_.duration / 60, 0)}}

… and the output looks like this:

PS C:\_Sample> $JSONInput.format |
>>     Select-Object -Property filename, format_long_name, @{Name = 'Runtime'; Expression = {'{0} min' -f [Math]::Round($_.duration / 60, 0)}}

filename                                     format_long_name Runtime
--------                                     ---------------- -------
D:\Movies\12 Angry Men (1957).mp4            QuickTime / MOV  96 min
D:\Movies\13 Relaxed And Nice Men (1958).mp4 QuickTime / MOV  64 min

That’s a kind of a strange requirement IMO. If you really need it anyway you may use string operations (full text search) like Select-String. :man_shrugging:t3:

1 Like

Hi Olaf,

Thanks for your detailed and insightful help with gaining a better understand on the use of the Where-Object.

I’ve been playing around with generating larger datasets to experiment with. So far do good. Combining json’s is a bit tricky though, but I’m working through it.

I’m going to run a full inventory on my media library this weekend and use that as my playground (dataset) for practicing how to run various searches.

This one works well, although I noticed some changes with the different json data…

This one was really just my way of doing a basic search against all the properties or values in the PowerShell object. Similar to searching the Windows file system for files or their “contents”.

Thanks again Olaf…