1

I'm trying to code a PowerShell script that can iterate through the whole table, find every column with multiple values (they're delimited either by ";" or "/"), and split them. But I don't have that much knowledge. Could anyone help me?

My table looks more or less like this:

Art. No. Attribute Bullet Point
1 a;b;c d/e/f
2 g;h; i/j/k/l
3 m;n;o;p;q r/s

Now I'd like to split the "attribute" and "bullet point" columns so that I can have something like this:

Art. No. Attribute1 Attribute 2 Attribute 3 Attribute 4 Bullet Point 1 Bullet Point 2 Bullet Point 3 Bullet Point 4
1 a b c d e f
2 g h i j k l
3 m n o p r s

As I said, I'm not that good at PowerShell scripts, so I actually couldn't do that much. I tried looking on the internet, but couldn't find anything really helpful.

4
  • 1
    this is the opposite of what you should be doing. you should be expanding on rows instead of expanding on columns Commented Jul 30, 2024 at 15:06
  • 1
    Is 4 the maximum number of attribute/bullet-point columns? As @SantiagoSquarzon suggests this is likely a bad idea - if you want to compare whether two rows have shared attributes you'll now need to compare each new column to each new column on the reference row. Can you tell us more about why you want to do it that way? Where is the resulting data going next? Commented Jul 30, 2024 at 15:33
  • @SantiagoSquarzon, I upvoted you comment as "expanding on rows" would indeed be a better approach for a single column/property expansion but is actually not easily to implement/design if it concerns multiple fields in a row that need to be expanded... Commented Jul 31, 2024 at 9:10
  • 1
    @iRon As long as you just do one property at a time it's pretty trivial to explode string properties into rows Commented Jul 31, 2024 at 9:41

2 Answers 2

1

Assuming you have a variable $objs with your data


foreach ($obj in $objs) {
    foreach ($property in $obj.PSObject.Properties.Name) {
        $SplittedValue = ([string]$obj.$property).Split( [char[]](';','/') )
        if ( $SplittedValue.Count -gt 1 ) {
            $i = 1
            foreach ($value in $SplittedValue) {
                Add-Member -InputObject $obj -MemberType NoteProperty -Name "$property$i" -Value $value
                $i++
            }
        }
    }
}
$properties = $objs | ForEach { $_.PSObject.Properties.Name } | select -Unique | sort
$objs = $objs | select -Property $properties
$objs | Format-Table -Property *

Output

Art. No. Attribute Attribute1 Attribute2 Attribute3 Attribute4 Attribute5 Bullet Point Bullet Point1 Bullet Point2 Bullet Point3 Bullet Point4
-------- --------- ---------- ---------- ---------- ---------- ---------- ------------ ------------- ------------- ------------- -------------
       1 a;b;c     a          b          c                                d/e/f        d             e             f                          
       2 g;h;      g          h                                           i/j/k/l      i             j             k             l            
       3 m;n;o;p;q m          n          o          p          q          r/s          r             s                                        
Sign up to request clarification or add additional context in comments.

2 Comments

This fails in row m;n;o;p;q. For this to work you need get the maximum number of columns beforehand. Your code also requires a [char[]] cast on .Split for pwsh 7 users.
@SantiagoSquarzon Thanks for your comment, I update my code.
1

To compliment the helpful answer from y y with some more background to add to your learning curve:

Delimiters

Let's start with defining the delimiters:

$Delimiters = ';', '/'

To avoid multiple embedded native PowerShell loop (that could make you script pretty expensive) we are going to use a regular expresion (regex). This allows us to split the concerned fields upon any of the delimiters in once and is not limited to a single character.

$RegexPattern = @($Delimiters).foreach{ [Regex]::Escape($_) } -Join '|'

The [basic concept] for the regular expression (https://en.wikipedia.org/wiki/Regular_expression#Basic_concepts) here is based on a:

Boolean "or"
A vertical bar separates alternatives. For example, gray|grey can match "gray" or "grey".

Note that as any of the $Delimiters could potential contain a special regex characters (as the | character itself) it is important to escape ([Regex]::Escape($_)) these delimiters first.

Input object

In general, it is advised to create a Minimal, Reproducible Example that prospective answerers can easily understand and use to reproduce the problem. This would usually included some code and and easy access to the input data which better would be a simple Csv expression (I uses Read-HtmlTable to pull your data):

Read-HtmlTable https://stackoverflow.com/q/78812449/1701026 -Table 0 | Export-Csv .\List.csv
Import-Csv .\List.csv

Art. No. Attribute Bullet Point
-------- --------- ------------
1        a;b;c     d/e/f
2        g;h;      i/j/k/l
3        m;n;o;p;q r/s

Iterate

To iterate through the $ObjectsList, I am going to use the ForEach-Object cmdlet which is able to do one-at-a-time processing. To find the property names and - values, it requires to use the intrinsic member PSObject property. For more background, see: Everything you wanted to know about PSCustomObject/Working with properties and e.g. this StackOverflow question

$Delimiters = ';', '/'
$RegexPattern = @($Delimiters).foreach{ [Regex]::Escape($_) } -Join '|'

Import-Csv .\List.csv | ForEach-Object {
    $Properties = [Ordered]@{}
    foreach ($Property in $_.PSObject.Properties) {
        $Properties[$Property.Name] = $Property.Value
        $Split = $Property.Value -Split $RegexPattern
        if ($Split.Count -gt 1) {
            $Index = 1
            foreach ($Item in $Split) {
                $Properties["$($Property.Name)$Index"] = $Item
                $Index++
            }
        }
    }
    [PSCustomObject]$Properties
} | Format-Table
Art. No. Attribute Attribute1 Attribute2 Attribute3 Bullet Point Bullet Point1 Bullet Point2 Bullet Point3
-------- --------- ---------- ---------- ---------- ------------ ------------- ------------- -------------
1        a;b;c     a          b          c          d/e/f        d             e             f
2        g;h;      g          h                     i/j/k/l      i             j             k
3        m;n;o;p;q m          n          o          r/s          r             s

Pitfall

But as you (and y y in the initial attempt 😊) might have noticed, there is a pitfall in this approach:

    Not all properties are displayed!

In general: even all properties still exist in the individual objects. PowerShell uses the first object to presume the properties that will follow in a pipeline. Note that this problem also exhibits in cmdlets as Export-Csv.

A purposed for a common workaround for this issue, can be found at: #13906 Add -UnifyProperties parameter to Select-Object.

function UniteProperties {
    $hash = [ordered]@{}
    $i = @($input)
    foreach ($obj in $i) {
        foreach ($p in $obj.psobject.properties) {
            $hash[$p.name] = $true
        }
    }
    $i | Select-Object ($hash.keys | ForEach-Object tostring)
}
ForEach-Object { ... } | UniteProperties | Format-Table *
Art. No. Attribute Attribute1 Attribute2 Attribute3 Bullet Point Bullet Point1 Bullet Point2 Bullet Point3 Bullet Point4 Attribute4 Attribute5
-------- --------- ---------- ---------- ---------- ------------ ------------- ------------- ------------- ------------- ---------- ----------
1        a;b;c     a          b          c          d/e/f        d             e             f
2        g;h;      g          h                     i/j/k/l      i             j             k             l
3        m;n;o;p;q m          n          o          r/s          r             s                                         p          q

This explains the initial comments from Santiago Squarzon and Mathias R. Jessen. Columns are named, it is difficult to address any sub-item (as. e.g. Attribute9) not knowing if it even exists.

1 Comment

Please accept (and/or up vote) any of the answers to your question or comment to it if anything is missing or unclear.

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.