1

I have an array, call it $x in powershell.

$x | ft -autosize

d     c     b
-     -     -
Apple       true
Apple true   
Banana       
Banana true  
Clark       true
David       true
David true  
Eddy  
Eddy        

I'm trying to combine items. The result I want in this case is:

$x | ft -autosize

d     c     b
-     -     -
Apple true  true
Banana true  
Clark       true
David true  true
Eddy  

Note: the blanks can represent false, but the array is typically blank when false, and TRUE when true. Lines are not always doubled up (as in Clark above)

I can loop thru the array, and collecting each element, 1 by 1, when I find a match (if any), but just keep thinking there has to be a better way.

3
  • What's wrong with your solution of iterating 1 by 1? Are you concerned about legibility? Optimization? If you already have a solution I don't see the need to think of a new solution... Commented Sep 10, 2019 at 19:59
  • 1
    The array is actually several hundred lines long, and the time it is taking to sort and combine it is noticed from the user's point of view. I just figured there had to be a better way then looping thru the array line by line. Commented Sep 10, 2019 at 20:04
  • 1
    I see. Three suggestion then. 1.) Include a minimum reproducible example someone could work off of. 2.) Post the solution you do have as that would likely provide a helpful starting point, and 3.) Edit your question title to reflect that you're looking for a solution that runs in linear time. (I'm assuming your solution runs in polynomial time, see en.wikipedia.org/wiki/Big_O_notation and try to deduce how many steps your solution uses. I'm guessing it includes a doubly nested for-loop of some sort?). Commented Sep 10, 2019 at 20:13

2 Answers 2

1

Group-Object is indeed the right cmdlet to use:

# Sample input (constructed from CSV data for convenience)
$x = @'
d,c,b
Apple,,true
Apple,true,
Banana,,  
Banana,true,
Clark,,true,
David,,true
David,true,
Eddy,,
Eddy,,
'@ | ConvertFrom-Csv

$x | Group-Object d | # Group the array of objects by their .d property
  ForEach-Object {    # Process each resulting group
    # For each .d value, construct a custom object that aggregates
    # the .c and .b property values and output it.
    [pscustomobject] @{
      d = $_.Name # The shared .d value
      c = $_.Group.c | Where-Object { $_ } # any non-empty .c values 
      b = $_.Group.b | Where-Object { $_ } # any non-empty .b values 
    }
  }

The above yields:

d      c    b
-      -    -
Apple  true true
Banana true 
Clark       true
David  true true
Eddy        
Sign up to request clarification or add additional context in comments.

2 Comments

I like the use of the Where-Object, much cleaner
Thanks, @jrider. Also note the shortcut syntax for creating the output object and the implicit output, which would allow for the entire command's output to be collected in an array implicitly - preferable to verbose and inefficient incremental "extending" (recreation with new element appended) of an array. Note that you never need @(...) to create array literals, and that (...) is preferable to $(...) in most cases.
1

The following should clean up your array in PowerShell by leveraging Group-Object

Create an array called $exampleArray:

$exampleArray = @()
$names = @("Apple", "Banana", "Clark", "David", "Eddy")
$tOrB = @("True","")
#just for an example
1..20 | % {

    $object = New-Object PSObject -Property @{
    #Get random Name
    d = $names[$(Get-Random -Maximum $names.Count)]
    #do true / blank -- random
    c = $tOrB[$(Get-Random -Maximum $tOrB.Count)]
    b = $tOrB[$(Get-Random -Maximum $tOrB.Count)]
    }

    $exampleArray += $object
}
$exampleArray

We can then group by d:

#group "combine" by column d
$group = $exampleArray | Group-Object d

Once grouped we can iterate through the grouped names and build a new array called $combined:

$combined = @()
$group | %{

    $object = New-Object PSObject -Property @{
        d = $_.Name
    }    
    if($_.Group.c -contains "True")
    {
        $object | Add-Member -MemberType NoteProperty -Name c -Value "True"
    }
    else
    {
        $object | Add-Member -MemberType NoteProperty -Name c -Value ""
    }
    if($_.Group.b -contains "True")
    {
        $object | Add-Member -MemberType NoteProperty -Name b -Value "True"
    }
    else
    {
        $object | Add-Member -MemberType NoteProperty -Name b -Value ""
    }    
    $combined += $object
}
$combined

Note: If you already have the array loaded (in this case $exampleArray), you might be better off just doing a Where-Object off of that array... This is dependent on what exactly you are attempting to accomplish. The answer I provided is most likely over-kill

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.