19

If I have an array:

1 1 1 2 2 3 4 4 4 4 5 5

How can I use Powershell to tell me how many of each element there are in that array?

To be a little more clear, I should have a separate count for each array element:

Element:Count

1:3

2:2

3:1

4:4

5:2

speedreeder
  • 497
  • 2
  • 4
  • 13

3 Answers3

36

You can use the Group-Object cmdlet:

PS> 1,1,1,2,2,3,4,4,4,4,5,5 | group

Count Name                      Group
----- ----                      -----
    3 1                         {1, 1, 1}
    2 2                         {2, 2}
    1 3                         {3}
    4 4                         {4, 4, 4, 4}
    2 5                         {5, 5}

If you want a hashtable for the items and their counts you just need a little ForEach-Object after it:

$array | group | % { $h = @{} } { $h[$_.Name] = $_.Count } { $h }
Joey
  • 344,408
  • 85
  • 689
  • 683
  • Thanks for this. What would be an elegant way to derive the `mode` (most frequently occurring value) of the original array? That is, given the OP's array `1 1 1 2 2 3 4 4 4 4 5 5`, I need to return `4`. – Ifedi Okonkwo Nov 04 '19 at 07:59
  • 1
    @IfediOkonkwo: `1,1,1,2,2,3,4,4,4,4,5,5 | group -NoElement | sort Count | select -last 1 -expandProperty Name`. A bit long, but each part of that pipeline does a single thing and composes well. Another option would be to separate getting the value and picking the last one with `... | group -NoElement | sort Count | % Name | select -last 1`. – Joey Nov 04 '19 at 11:15
  • @Joey: thanks a million. Before noticing your response, I'd cobbled something together, that worked, though more unwieldy than yours: `$hash = $Array | group | % { $h = @{} } { $h[$_.Name] = $_.Count } { $h }; $mode = $hash.GetEnumerator() | Sort-Object -Property Value -Descending | Select-Object -First 1` – Ifedi Okonkwo Nov 06 '19 at 20:11
  • @mklement0: I completely agree with you. But then, it's only human that laziness sometimes gets the better of you. Hopefully, google can still lead searchers to this very thread ;) – Ifedi Okonkwo Nov 06 '19 at 20:13
  • 1
    @IfediOkonkwo: Alternatively, we can [do the right thing](https://stackoverflow.com/q/58738500/45375). Note that your solution doesn't handle the case where _multiple_ values occur most frequently. – mklement0 Nov 06 '19 at 21:10
  • Wow! You put me to shame by that elaborate self-answered effort! Well done and +1 for both question and answer. – Ifedi Okonkwo Nov 12 '19 at 06:11
7

You can adjust the output and format it as you like:

PS> $ht= 1,1,1,2,2,3,4,4,4,4,5,5 | Group-Object -AsHashTable -AsString
PS> $ht

Name                           Value
----                           -----
2                              {2, 2}
4                              {4, 4, 4, 4}
5                              {5, 5}
1                              {1, 1, 1}
3                              {3}


PS> $ht['1']
1
1
1
Shay Levy
  • 121,444
  • 32
  • 184
  • 206
6

Joey's helpful answer provides the crucial pointer: use the Group-Object cmdlet to group input objects by equality.
(To group them by one ore more of their property values instead, use -Property).

Group-Object outputs [Microsoft.PowerShell.Commands.GroupInfo] objects that each represent a group of equal input objects, whose notable properties are:

  • .Values ... the value(s) that define the group, as a [System.Collections.ArrayList] instance (which has just 1 element in the case at hand, since the input objects as a whole are used to form the groups).

  • .Count ... the count of objects in that group.

If, as in this case, there is no need to collect the individual group members as part of each group,
-NoElement can be used for efficiency.

You're free to further process the group objects as needed; to get the specific output format stipulated in your question, you can use Select-Object with a calculated property.

To put it all together:

PS> 1, 1, 1, 2, 2, 3, 4, 4, 4, 4, 5, 5 | Group-Object -NoElement |
      Select-Object @{ n='Element:Count'; e={ '{0}:{1}' -f $_.Values[0], $_.Count } }

Element:Count
-------------
1:3
2:2
3:1
4:4
5:2
mklement0
  • 382,024
  • 64
  • 607
  • 775