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

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.