Tuples/ArrayList of pairs

依然范特西╮ 提交于 2019-12-10 23:00:03

问题


I'm essentially trying to create a list of pairs which is proving frustratingly difficult

Note before anyone mentions Hashtables that there will be duplicates which I don't care about.

For example, if I do

$b = @{"dog" = "cat"}

I get

Name                           Value
----                           -----
dog                            cat

which is good. However, I'm then unable to add the likes of

$b += @{"dog" = "horse"}

Item has already been added. Key in dictionary: 'dog' Key being added: 'dog'

I'm just trying to create a table of data which I can add to using something like .Add() or +=.


回答1:


Persistent13's helpful answer offers an effective and efficient solution - albeit a slightly obscure one.

Creating an array of hashtables is the simplest solution, though it's important to note that "extending" an array means implicitly recreating it, given that arrays are fixed-size data structures, which can become a performance problem with many iterations:

# Using array-construction operator ",", create a single-element array
# containing a hashtable
$b = , @{ dog = "cat"} 

# "Extend" array $b by appending another hashtable.
# Technically, a new array must be allocated that contains the original array's
# elements plus the newly added one.
$b += @{ dog = "horse"}

This GitHub issue discusses a potential future enhancement to make PowerShell natively support an efficiently extensible list-like data type or for it to even default to such a data type. (As of Windows PowerShell v5.1 / PowerShell Core 6.2.0, PowerShell defaults to fixed-size arrays, of type [object[]]).




回答2:


I believe a list of hashtables should accomplish what you are after.

$ht = @{foo='bar'}
$list = [System.Collections.Generic.List[hashtable]]::new()
$list.Add($ht)
$list.Add($ht)
$list.Add($ht)
$list



回答3:


If you want a list of tuples I'd recommend actually building a list of tuples:

$list = @()

$tuple1 = New-Object 'Tuple[String,String]' 'dog', 'cat'
$tuple2 = New-Object 'Tuple[String,String]' 'dog', 'horse'

$list += $tuple1
$list

# Output:
#
# Item1 Item2 Length
# ----- ----- ------
# dog   cat        2

$list += $tuple2
$list

# Output:
#
# Item1 Item2 Length
# ----- ----- ------
# dog   cat        2
# dog   horse      2

Note that in this example $list is a regular array, meaning that, as @mklement0 pointed out in his answer, appending to it with the += assignment operator will re-create the array with size increased by 1, put the new item in the new empty slot, then replace the original array. For a small number of append operations this usually isn't a big issue, but with increasing number of append operations the performance impact becomes significant.

Using an ArrayList instead of a plain array avoids this issue:

$list = New-Object Collections.ArrayList

$tuple1 = New-Object 'Tuple[String,String]' 'dog', 'cat'
$tuple2 = New-Object 'Tuple[String,String]' 'dog', 'horse'

$list.Add($tuple1) | Out-Null
$list

# Output:
#
# Item1 Item2 Length
# ----- ----- ------
# dog   cat        2

$list.Add($tuple2) | Out-Null
$list

# Output:
#
# Item1 Item2 Length
# ----- ----- ------
# dog   cat        2
# dog   horse      2

The Add() method of ArrayList objects outputs the index where the item was appended. Out-Null suppresses that output that is in most cases undesired. If you want to work with these index numbers you can collect them in a variable instead of discarding them ($i = $list.Add($t1)).

If you want to avoid having to specify the type of a new tuple all the time you can wrap it into a reusable function like this:

function New-Tuple {
    Param(
        [Parameter(Mandatory=$true)]
        [ValidateCount(2,2)]
        [string[]]$Values
    )

    New-Object 'Tuple[String,String]' $Values
}

$tuple1 = New-Tuple 'dog', 'cat'
$tuple2 = New-Tuple 'dog', 'horse'

or, in a more generic way, like this:

function New-Tuple {
    Param(
        [Parameter(
            Mandatory=$true,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true
        )]
        [ValidateCount(2,20)]
        [array]$Values
    )

    Process {
        $types = ($Values | ForEach-Object { $_.GetType().Name }) -join ','
        New-Object "Tuple[$types]" $Values
    }
}

$tuples = ('dog', 'cat'), ('dog', 'horse') | New-Tuple



回答4:


Thanks to mklement0 as below is probably the simplest solution

$b = , @{ dog = "cat"} 
$b += @{ dog = "horse"}

And Persistent13's method also yields an easy route

$ht = @{foo='bar'}
$list = [System.Collections.Generic.List[hashtable]]::new()
$list.Add($ht)
$list.Add($ht)
$list.Add($ht)
$list

I suppose i was just surprised that there wasn't a more ingrained way to get what i feel is a pretty basic/standard object type



来源:https://stackoverflow.com/questions/54373785/tuples-arraylist-of-pairs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!