Saltar al contenido

¿Por qué debería usar un hash ordenado en PowerShell?

Solución:

La razón de un ordered diccionario es para fines de visualización / encasillado. Por ejemplo, si desea lanzar su hashtable a un PSCustomObject y desea que sus claves estén en el orden en que las ingresa, utilice ordered.

El caso de uso aquí es cuando usa Export-Csv, los encabezados están en el orden correcto. Este es solo un ejemplo que se me ocurre. Por diseño, el hashtable type no se preocupa por el orden en que ingresa claves / valores y será diferente cada vez que lo muestre en el flujo de éxito.

Un caso de uso adicional para el ordered diccionario: puede tratar su tabla hash como una matriz y usar accesos numéricos para encontrar elementos, como $myOrderedHash[-1] tomará el último elemento agregado al diccionario.

Permítanme complementar la útil respuesta de TheIncorrigible1 con una perspectiva más amplia:

tl; dr

  • La mayor parte del tiempo quieres [ordered] @{ ... } ([System.Collections.Specialized.OrderedDictionary]) (PSv3 +):

    • Proporciona enumeración de las entradas. en el orden en que fueron definidos (también reflejado en el .Keys y .Values propiedades de la colección).
    • También permite acceder a las entradas índice, como una matriz.
  • Normalmente, puede utilizar [ordered] @{ ... } indistintamente con @{ ... }, la tabla hash habitual, [hashtable] alias [System.Collections.Hashtable], porque ambos tipos implementan el [IDictionary] interfaz, que es como se escriben normalmente los parámetros que aceptan tablas hash.

La penalización por desempeño que paga por usar [ordered] es despreciable.


Algunos antecedentes:

Para técnico razones, la implementación más eficiente de una tabla hash (tabla hash) Es para dejar que el orden de las entradas sea el resultado de detalles de implementacion, sin garantizar ningún pedido en particular a la persona que llama.

Esto está bien para casos de uso donde todo lo que hace es realizar búsquedas aisladas por clave, donde el orden entre claves (entradas) es irrelevante.

Sin embargo, a menudo le importa el orden de las entradas:

  • en el caso más simple, por monitor propósitos; hay algo desconcertante en ver el orden de las definiciones desordenado; p.ej:

    @{ one = 1; two = 2; three = 3 } 
    
    Name                           Value
    ----                           -----
    one                            1
    three                          3   # !! 
    two                            2
    
  • lo que es más importante, es posible que la enumeración de entradas deba ser predecible para un procesamiento programático adicional; p. ej. (nota: estrictamente hablando, el orden de propiedad no importa en JSON, pero nuevamente es importante para el observador humano):

    # No guaranteed property order.
    PS> @{ one = 1; two = 2; three = 3 } | ConvertTo-Json
    {
     "one": 1,
     "three": 3, # !!
     "two": 2
    }
    
    # Guaranteed property order.
    PS> [ordered] @{ one = 1; two = 2; three = 3 } | ConvertTo-Json
    {
      "one": 1,
      "two": 2,
      "three": 3
    }
    

Es lamentable que la sintaxis literal de tabla hash de PowerShell, @{ ... }, no predeterminado a [ordered][1], pero es demasiado tarde para cambiar eso.

Hay un contexto en el que [ordered] es implícito, sin embargo: si lanzas una tabla hash literal para [pscustomobject] para crear un objeto personalizado:

[pscustomobject] @{ ... } es azúcar sintáctica por [pscustomobject] [ordered] @{ ... }; es decir, las propiedades del objeto personalizado resultante se ordenan según el orden de entrada en el literal de tabla hash; p.ej:

PS> [pscustomobject] @{ one = 1; two = 2; three = 3 }

one two three  # order preserved!
--- --- -----
  1   2     3

Tenga en cuenta, sin embargo, que este solo funciona exactamente como se muestra arriba: si el yeso se aplicó directamente a una tabla hash literal; si usa un variable para almacenar la tabla hash en primer lugar o si simplemente incluye el literal en (...) el pedido se pierde:

PS> $ht = @{ one = 1; two = 2; three = 3 }; [pscustomobject] $ht

one three two  # !! Order not preserved.
--- ----- ---
  1     3   2


PS> [pscustomobject] (@{ one = 1; two = 2; three = 3 }) # Note the (...)

one three two  # !! Order not preserved.
--- ----- ---
  1     3   2

Por lo tanto, si construyes una tabla hash iterativamente primero y luego enviarlo a [pscustomobject], debes comenzar con un [ordered] tabla de picadillo para obtener un orden predecible de propiedades; esta técnica es útil, porque es más fácil crear entradas de tabla hash que agregar propiedades a un objeto personalizado; p.ej:

$oht = [ordered] @{} # Start with an empty *ordered* hashtable 
# Add entries iteratively.
$i = 0
foreach ($name in 'one', 'two', 'three') {
  $oht[$name] = ++$i
}
[pscustomobject] $oht # Convert the ordered hashtable to a custom object

Finalmente, tenga en cuenta que [ordered] solo se puede aplicar a hashtable literal; no puede usarlo para convertir una tabla hash regular preexistente en una ordenada (lo cual no tendría ningún sentido de todos modos, porque no tiene un orden definido para empezar):

PS> $ht = @{ one = 1; two = 2; three = 3 }; [ordered] $ht # !! Error
...
The ordered attribute can be specified only on a hash literal node.
...

En otros comentarios: Ni las tablas hash ordenadas ni regulares enumeran sus entradas cuando se envían a través de la canalización; se envían en su conjunto.
Para enumerar las entradas, utilizar el .GetEnumerator() método; p.ej:

@{ one = 1; two = 2; three = 3 }.GetEnumerator() | ForEach-Object { $_.Value }
1
3  # !!
2

En cuanto a impacto en el rendimiento del uso [ordered]:

Como se señaló, es despreciable; aquí hay algunos tiempos de muestra, promediados en 10,000 ejecuciones, usando Time-Command:

Time-Command -Count 10,000 { [email protected]{one=1;two=2;three=3;four=4;five=5;six=6;seven=7;eight=8;nine=9}; foreach($k in $ht.Keys){$ht.$k} }, 
                           { $ht=[ordered] @{one=1;two=2;three=3;four=4;five=5;six=6;seven=7;eight=8;nine=9}; foreach($k in $ht.Keys){$ht.$k} }

Tiempos de muestra (Windows PowerShell 5.1 en Windows 10, VM de un solo núcleo):

Command                 TimeSpan         Factor
-------                 --------         ------
[email protected]{one=1;two=2;th... 00:00:00.0000501 1.00  
$ht=[ordered] @{one=... 00:00:00.0000527 1.05  

Es decir, [ordered] ascendió a una desaceleración mera del 5%.


[1] TheIncorrigible1 señala uno aspecto complicado específico de [ordered] tablas hash:

Con numérico teclas, distinguiendo entre un llave y un índice puede volverse complicado; forzar la interpretación de un número como llave, enviarlo a [object]:

# Ordered hashtable with numeric keys.
PS> $oht = [ordered] @{ 1 = 'one'; 2 = 'two' }

PS> $oht[1]  # same as $oht.1 - interpreted as *index* -> 2nd entry
two

PS> $oht[[object] 1] # interpreted as *key* -> 1st entry.
one

Dicho esto, las teclas numéricas no son comunes y, para mí, el beneficio de utilizar la enumeración predecible de forma predeterminada supera este problema menor.

El tipo de marco .NET subyacente [ordered], System.Collections.Specialized.OrderedDictionary, ha estado disponible desde v1, por lo que PowerShell podría haberlo elegido como la implementación predeterminada para @{ ... } desde el principio, incluso en PowerShell v1.
Sin embargo, dado el compromiso de PowerShell con la compatibilidad con versiones anteriores, cambiar el valor predeterminado ya no es una opción, ya que eso podría romper el código existente.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)


Tags : /

Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *