2

Ultimetly I'm trying to 3 arrays for like items and put those like items together by their color. But for now I'm just trying to check if one line in a array, $line, does not match anything in the other array, $colors,

$itemsofCars =@()
$cars = @()
$colors =  Get-Content "C:\Newfolder\colors.txt"
$typefile = Get-Content "C:\Newfolder\cars_not_formatted.txt"
$carBlock = @("1")
foreach ($line in $typefile)

        {
            if($line.Contains("_Wheel") -or
               $line.Contains("Doors") -or
               $line.Contains("trunk") -or
               $line.Contains("hood") -or
               $line.Contains("coDriver") -or
               $line.Contains("_driver")
               ) 
            { 
                   $itemsofCars += $line
             }
          else
          {
             $cars += $line
          }               
        }
#$itemsofCars
#$cars
#foreach ($car in $cars)
#  {
#    Add-Content 'C:\Newfolder\test.txt'  $itemsofCars 
# }

 foreach ($line in $cars)

        {
            #trying to check if the line in car contains a color, if not then add to carBlock
            if($line.ToUpper() -notcontains $colors.ToUpper())
            {
                #making sure there isn't duplicates
                if($carBlock.ToUpper() -notcontains $car.ToUpper())
                {
                    $carBlock = $line 
                    $carBlock
                }
            }
            else
            {
                
            }
        }

this is a part of cars file

BRDM
BRDM_Wheel
BRDM_Doors_Driver
BRDM_Doors_coDriver
BRDM_Doors_hood
BRDM_Doors_trunk
BRDM_Doors_Driver_Woodland
BRDM_Doors_coDriver_Woodland
BRDM_Doors_hood_Woodland
BRDM_Doors_trunk_Woodland
BRDM_Doors_Driver_black
BRDM_Doors_coDriver_black
BRDM_Doors_hood_black
BRDM_Doors_trunk_black
BRDM_Doors_Driver_Pixel
BRDM_Doors_coDriver_Pixel
BRDM_Doors_hood_Pixel
BRDM_Doors_trunk_Pixel
BRDM_Doors_Driver_Camouflage
BRDM_Doors_coDriver_Camouflage
BRDM_Doors_hood_Camouflage
BRDM_Doors_trunk_Camouflage
BRDM_Doors_Driver_Flecktarn
BRDM_Doors_coDriver_Flecktarn
BRDM_Doors_hood_Flecktarn
BRDM_Doors_trunk_Flecktarn
BRDM_Woodland
BRDM_black
BRDM_Pixel
BRDM_Camouflage
BRDM_Flecktarn

This is the colors file

Woodland
Black
Pixel
Camouflage
Flecktarn
ZSU
Grey
Desert
Chrome
Winter
Scull
Blue
Orange
Red
Green
lightgreen
kamo
union
white
Gold
Samurai
grey
blue
kamo2
BIOHAZARD
CAMOgreen
carbon
Flora
GVILORD
skul
beige
chaki2
MCHS
les
umbr

I've rearranged the code numerus times and couldn't get it to work. What can I do to make it happen?

Remp2012
  • 31
  • 3
  • Why didn't you update the earlier version of this question? [Why is my PowerShell script not printing strings to a file?](https://stackoverflow.com/q/76387846/9196560) – Olaf Jun 03 '23 at 22:27
  • because I got it and then ran into another problem – Remp2012 Jun 03 '23 at 23:21
  • As an aside: [try to avoid using the increase assignment operator (`+=`) to create a collection](https://stackoverflow.com/a/60708579/1701026) as it might get pretty expensive. – iRon Jun 04 '23 at 18:51

3 Answers3

2

Your code seem overly complicated for what you're trying to achieve, you can create regex patterns from your colors.txt file and all the parts in your if condition then you can use a switch with the -Regex and -File flags to read and match line-by-line against these patterns, to then store them in their own collection.

Lastly, enumerate each collection and output to a file.

$parts = ('_Wheel', 'Doors', 'trunk', 'hood', 'coDriver', '_driver' | ForEach-Object { [regex]::Escape($_) }) -join '|'
$colors = (Get-Content .\colors.txt | ForEach-Object { [regex]::Escape($_) }) -join '|'

$result = @{
    itemsofCars = [System.Collections.Generic.List[string]]::new()
    cars        = [System.Collections.Generic.List[string]]::new()
    colorsofCar = [System.Collections.Generic.List[string]]::new()
}

switch -Regex -File .\cars_not_formatted.txt {
    $parts { $result['itemsofCars'].Add($_); continue }
    $colors { $result['colorsofCar'].Add($_); continue }
    default { $result['cars'].Add($_) }
}

$result.GetEnumerator() | ForEach-Object { $_.Value | Set-Content ($_.Key + '.txt') }

You could also use 3 steppable pipelines instead of 3 lists if you need to preserve memory:

# `$parts` and `$colors` are defined here the same way

$result = @{
    itemsofCars = { Set-Content .\itemsofCars.txt }.GetSteppablePipeline()
    cars        = { Set-Content .\cars.txt }.GetSteppablePipeline()
    colorsofCar = { Set-Content .\colorsofCars.txt }.GetSteppablePipeline()
}

$result.GetEnumerator() | ForEach-Object { $_.Value.Begin($true) }

switch -Regex -File .\cars_not_formatted.txt {
    $parts { $result['itemsofCars'].Process($_); continue }
    $colors { $result['colorsofCar'].Process($_); continue }
    default { $result['cars'].Process($_) }
}

$result.GetEnumerator() | ForEach-Object { $_.Value.End() }
Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
  • I didn't do that because I didn't understand it. I'm really new to powershell. all of $result is giving me "cannot call a method on a null-valued expression" – Remp2012 Jun 03 '23 at 23:20
  • are you even able to switch arrays? – Remp2012 Jun 04 '23 at 04:03
  • @Remp2012, the (PowerShell) [Pipeline](https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_pipelines) is really powerful and specific to PowerShell, you might want to read [Mastering the (steppable) pipeline](https://devblogs.microsoft.com/powershell-community/mastering-the-steppable-pipeline/) for your background information. – iRon Jun 04 '23 at 17:14
  • @Remp2012 I'm not sure what you mean, this code works as is. You just need to change the txt paths for your actual paths – Santiago Squarzon Jun 04 '23 at 18:00
0

Output from two arrays; colors and parts—both the lines that do match, and the lines that do not match.

Use a foreach nested loop to control and list out the matching lines from each array using the -match comparison operator.

Also are two Compare-Object commands to list out the values from each array that do not match any of the lines in the other array—the non-matches.

PowerShell (matches)

$Parts = Get-Content "E:\Parts.txt";
$Colors = Get-Content "E:\Colors.txt";

## -- Show matching part to matching color
foreach ($part In $Parts) {
    foreach ($color In $Colors) {
        if ($part -match $color) {
            "Part: $($part.ToUpper()), Color: $($color.ToUpper())"
        }
    }
};

Output

Part: BRDM_DOORS_DRIVER_WOODLAND, Color: WOODLAND
Part: BRDM_DOORS_CODRIVER_WOODLAND, Color: WOODLAND
Part: BRDM_DOORS_HOOD_WOODLAND, Color: WOODLAND
Part: BRDM_DOORS_TRUNK_WOODLAND, Color: WOODLAND
Part: BRDM_DOORS_DRIVER_BLACK, Color: BLACK
Part: BRDM_DOORS_CODRIVER_BLACK, Color: BLACK
Part: BRDM_DOORS_HOOD_BLACK, Color: BLACK
Part: BRDM_DOORS_TRUNK_BLACK, Color: BLACK
Part: BRDM_DOORS_DRIVER_PIXEL, Color: PIXEL
Part: BRDM_DOORS_CODRIVER_PIXEL, Color: PIXEL
Part: BRDM_DOORS_HOOD_PIXEL, Color: PIXEL
Part: BRDM_DOORS_TRUNK_PIXEL, Color: PIXEL
Part: BRDM_DOORS_DRIVER_CAMOUFLAGE, Color: CAMOUFLAGE
Part: BRDM_DOORS_CODRIVER_CAMOUFLAGE, Color: CAMOUFLAGE
Part: BRDM_DOORS_HOOD_CAMOUFLAGE, Color: CAMOUFLAGE
Part: BRDM_DOORS_TRUNK_CAMOUFLAGE, Color: CAMOUFLAGE
Part: BRDM_DOORS_DRIVER_FLECKTARN, Color: FLECKTARN
Part: BRDM_DOORS_CODRIVER_FLECKTARN, Color: FLECKTARN
Part: BRDM_DOORS_HOOD_FLECKTARN, Color: FLECKTARN
Part: BRDM_DOORS_TRUNK_FLECKTARN, Color: FLECKTARN
Part: BRDM_WOODLAND, Color: WOODLAND
Part: BRDM_BLACK, Color: BLACK
Part: BRDM_PIXEL, Color: PIXEL
Part: BRDM_CAMOUFLAGE, Color: CAMOUFLAGE
Part: BRDM_FLECKTARN, Color: FLECKTARN

PowerShell (no matches)

$Parts = Get-Content "E:\Parts.txt";
$Colors = Get-Content "E:\Colors.txt";

$Matches = foreach ($part In $Parts) {
    foreach ($color In $Colors) {
        if ($part -match $color) {
            "$($part.ToUpper()), $($color.ToUpper())"
        }
    }
};

## Show non-matching parts
(Compare-Object $matches.ForEach({$_.split(",")[0]}) $Parts).InputObject | Select-Object @{N="Mismatch part";E={$_.ToUpper()}};

## Show non-matching colors
(Compare-Object $matches.ForEach({$_.split(",")[1]}).Trim() $Colors | 
    Where-Object {$_.SideIndicator -eq "=>"}).InputObject | 
        Select-Object @{N="Mismatch color";E={$_.ToUpper()}};

Output

Mismatch part      
-------------      
BRDM               
BRDM_Wheel         
BRDM_Doors_Driver  
BRDM_Doors_coDriver
BRDM_Doors_hood    
BRDM_Doors_trunk   

Mismatch color
--------------
ZSU           
Grey          
Desert        
Chrome        
Winter        
Scull         
Blue          
Orange        
Red           
Green         
lightgreen    
kamo          
union         
white         
Gold          
Samurai       
grey          
blue          
kamo2         
BIOHAZARD     
CAMOgreen     
carbon        
Flora         
GVILORD       
skul          
beige         
chaki2        
MCHS          
les           
umbr          

Supporting Resources

Bitcoin Murderous Maniac
  • 1,209
  • 1
  • 14
  • 27
  • that was only part of the cars and their parts. its a long file so I didn't give all of it. – Remp2012 Jun 03 '23 at 23:25
  • I'm not worried about it running slow. I'm just very new to powershell. I have relatively longer explanation of what my final result will be here: https://stackoverflow.com/q/76387846/9196560 – Remp2012 Jun 03 '23 at 23:30
  • The foreach is a nested loop giving iterated values elements to compare, etc. – Bitcoin Murderous Maniac Jun 03 '23 at 23:44
  • I was going step by step to get to my end goal. and what I posted is using what I know from c++ and java to code in powershell. but its been 10+ years since I've actually coded so the rust is very thick. my output will ultimately be what the xml file showed in the other post. I'm just trying to get there. – Remp2012 Jun 03 '23 at 23:52
  • @Remp2012 Actually, there you go, check out the latest edit, I did have an issue with the no match/mismatches, so I've fixed that using `Compare-Object`, refresh to see the latest detail. I'm removing my previous unnecessary comments for cleanup noise purposes. – Bitcoin Murderous Maniac Jun 04 '23 at 15:04
0

For this issue you might consider to use ScriptProperty members which late bind the property value to the object which means you might add items to the $Colors and $Parts list after you created the $Cars list

$Names  = 'BRDM', 'BRDM_Wheel', 'BRDM_Doors_Driver', 'BRDM_Doors_coDriver', 'BRDM_Doors_hood', 'BRDM_Doors_trunk', 'BRDM_Doors_Driver_Woodland', 'BRDM_Doors_coDriver_Woodland', 'BRDM_Doors_hood_Woodland', 'BRDM_Doors_trunk_Woodland', 'BRDM_Doors_Driver_black', 'BRDM_Doors_coDriver_black', 'BRDM_Doors_hood_black', 'BRDM_Doors_trunk_black', 'BRDM_Doors_Driver_Pixel', 'BRDM_Doors_coDriver_Pixel', 'BRDM_Doors_hood_Pixel', 'BRDM_Doors_trunk_Pixel', 'BRDM_Doors_Driver_Camouflage', 'BRDM_Doors_coDriver_Camouflage', 'BRDM_Doors_hood_Camouflage', 'BRDM_Doors_trunk_Camouflage', 'BRDM_Doors_Driver_Flecktarn', 'BRDM_Doors_coDriver_Flecktarn', 'BRDM_Doors_hood_Flecktarn', 'BRDM_Doors_trunk_Flecktarn', 'BRDM_Woodland', 'BRDM_black', 'BRDM_Pixel', 'BRDM_Camouflage', 'BRDM_Flecktarn'
$Colors = 'Woodland', 'Black', 'Pixel', 'Camouflage', 'Flecktarn', 'ZSU', 'Grey', 'Desert', 'Chrome', 'Winter', 'Scull', 'Blue', 'Orange', 'Red', 'Green', 'lightgreen', 'kamo', 'union', 'white', 'Gold', 'Samurai', 'grey', 'blue', 'kamo2', 'BIOHAZARD', 'CAMOgreen', 'carbon', 'Flora', 'GVILORD', 'skul', 'beige', 'chaki2', 'MCHS', 'les', 'umbr'
$Parts  = 'Wheel', 'Doors', 'trunk', 'hood', 'coDriver', 'driver'
$ColorScript = { $Colors.Where{ $This.Name.Split('_') -Contains $_ } }
$PartsScript = {  $Parts.Where{ $This.Name.Split('_') -Contains $_ } }
# $Cars = Get-Content .\cars_not_formatted.txt |Foreach-Object { ...
$Cars = $Names |Foreach-Object { [PSCustomObject]@{ Name = $_ } } |
 Add-Member -MemberType ScriptProperty -Name Color -Value $ColorScript -PassThru |
 Add-Member -MemberType ScriptProperty -Name Parts -Value $PartsScript -PassThru
$Cars |Where-Object Parts -Contains hood

Name                       Color      Parts
----                       -----      -----
BRDM_Doors_hood                       {Doors, hood}
BRDM_Doors_hood_Woodland   Woodland   {Doors, hood}
BRDM_Doors_hood_black      Black      {Doors, hood}
BRDM_Doors_hood_Pixel      Pixel      {Doors, hood}
BRDM_Doors_hood_Camouflage Camouflage {Doors, hood}
BRDM_Doors_hood_Flecktarn  Flecktarn  {Doors, hood}
$Cars |Where-Object Color -eq Woodland |Select-Object -Expand Name

BRDM_Doors_Driver_Woodland
BRDM_Doors_coDriver_Woodland
BRDM_Doors_hood_Woodland
BRDM_Doors_trunk_Woodland
BRDM_Woodland 

If you don't want to do "(PSCustom)Object Oriented Programming", you might even consider to add the ScriptProperty members directly to the strings which are actually (PowerShell) objects by itself. See e.g. $Cars[0].PSTypeNames.
The added (string) members will not show up in the string output and easily disappear when the be processed:

$ColorScript = { $Colors.Where{ $This.Split('_') -Contains $_ } }
$PartsScript = {  $Parts.Where{ $This.Split('_') -Contains $_ } }
# $Cars = Get-Content .\cars_not_formatted.txt | ...
$Cars = $Names |
 Add-Member -MemberType ScriptProperty -Name Color -Value $ColorScript -PassThru |
 Add-Member -MemberType ScriptProperty -Name Parts -Value $PartsScript -PassThru
$Cars |Where-Object Color -eq Black
BRDM_Doors_Driver_black
BRDM_Doors_coDriver_black
BRDM_Doors_hood_black
BRDM_Doors_trunk_black
BRDM_black

As the two ScriptProperty members are virtually the same and it is a bad practice to directly (without parameters) pull the $Color and $Parts list from the parent scope, you might also consider to use a ScriptMethod and pass the concerned item list.

$Cars = $Names |Add-Member -MemberType ScriptMethod -Name ListItem -Value {
    $Args |Foreach-Object { $_.Where{ $This.Split('_') -Contains $_ } }
}  -PassThru

$Cars |Where-Object { $_.ListItem($Colors) -eq 'Pixel' }
BRDM_Doors_Driver_Pixel
BRDM_Doors_coDriver_Pixel
BRDM_Doors_hood_Pixel
BRDM_Doors_trunk_Pixel
BRDM_Pixel

Note that unfortunately this $Cars |Where-Object ContainsItem($Colors) -eq 'Pixel' doesn't work as expected, see: #8224 "Where-Object -Property ..." should not accept method names because they're not properties

iRon
  • 20,463
  • 10
  • 53
  • 79