Question [Astuce]Création de structure de donnée.
- Laurent Dardenne
- Auteur du sujet
- Hors Ligne
- Modérateur
-
Réduire
Plus d'informations
- Messages : 6311
- Remerciements reçus 68
il y a 17 ans 5 mois #3002
par Laurent Dardenne
Tutoriels PowerShell
[Astuce]Création de structure de donnée. a été créé par Laurent Dardenne
Il est possible de créer une structure de donnée complexe à partir d'une hastable, ce qui peut faciliter certaines opérations.
Le principe d'une hastable est d'associer une clé unique à un objet, cet objet pouvant être un type simple ,un entier une chaîne mais aussi un type plus complexe tel qu'un tableau ou une collection.
Le principe étant d'imbriquer une hashtable comme valeur d'une entrée d'une hastable. Ceci permet de créer la structure suivante :
[code:1]
#Structure 1
EtatCivil=
Nom
DateNaissance
Adresse[]
#Structure 2
Profile=
Script
Quota
#Structure 3 combinant les 2 précédentes
Utilisateur =
EtatCivil
Profile
[/code:1]
Sa mise en oeuvre se faisant de la manière suivante :
[code:1]
#Crée la Hastable.
$Utilisateur=@{}
#Crée une structure de donnée autour d'une Hastable.
#Elle référence une hastable imbriquée (EtatCivil), elle même imbriquant un tableau (Adresse)
#Notez que le type de donnée de chaque entrée n'est pas figé.
$Utilisateur.EtatCivil=@{Nom=\"\";DateNaissance=\"\";Adresse=(new-object String[] 4)}
[/code:1]
On crée un tableau en utilisant le constructeur de System.Array ce qui évite la construction : Adresse=@($null,$null,$null,$null)
[code:1]
#Idem mais avec un tableau à 2 dimension, de 2 sur 2.
#$Utilisateur.EtatCivil=@{Nom=\"\";Age=0;DateNaissance=\"\";Adresse=(new-object \"String[,]\" 2,2)}
$Utilisateur.EtatCivil.Nom=\"Durand\"
$Utilisateur.EtatCivil.DateNaissance=([DateTime]::Now).AddMonths(12*-25)
$Utilisateur.EtatCivil.Adresse[1]=\"15 rue du faubourg \"
$Utilisateur.EtatCivil.Adresse[2]=\"Saint-Honoré\"
$Utilisateur.EtatCivil.Adresse[3]=\"75008 Paris\"
#On crée une seconde hashtable
$Utilisateur.Profile=@{}
#Recherche uniquement les fichiers du répertoire courant
$Files=Dir *.*|Where {!$_.PSIsContainer}
#Dans ce cas on peut ajouter des champs au fur et à mesure
# et sans passer par une initialisation
$Utilisateur.Profile.Script=$Files[1]
$Utilisateur.Profile.Quota=200
[/code:1]
Ce qui nous donne :
[code:1]
$Utilisateur
Name Value
----
Profile {Script, Quota}
EtatCivil {Adresse, DateNaissance, Nom}
$Utilisateur.EtatCivil
Name Value
----
Adresse {, 15 rue du faubourg , Saint-Honoré, 75008 Paris}
DateNaissance 16/10/1983 19:56:44
Nom Durand
[/code:1]
Il reste possible d'ajouter des membres synthétiques à chaque structure :
[code:1]
#Le contenu est un scriptbloc calculant l'age
$Utilisateur.EtatCivil.Age={ ((Get-date) - $Utilisateur.EtatCivil.DateNaissance).TotalDays /365 -as [int]}
#Exécute le scriptbloc pour calculer l'age.
&$Utilisateur.EtatCivil.Age
#Supprime la clé Age
$Utilisateur.EtatCivil.Remove(\"Age\"«»)
# On ajoute un membre synthétique de type méthode sur l'objet référencé par la clé EtatCivil
$Utilisateur.EtatCivil=$Utilisateur.EtatCivil|Add-member ScriptMethod Age { ((Get-date) - $this.DateNaissance).TotalDays /365 -as [int]} -Passthru
$Utilisateur.EtatCivil|gm |Sort name
$Utilisateur.EtatCivil.Age()
[/code:1]
Ce dernier appel étant plus pratique que celui d'un scriptblock.
Puisque que notre objet $Utilisateur n'est pas un type personnalisé il n'est pas possible d'ajouter un nom de type dans la collection PsObject.TypeNames, on ajoute donc un membre synthétique afin de porter cette information( si besoin est):
[code:1]
$Utilisateur.Profile=$Utilisateur.Profile|Add-member NoteProperty PSTypeName \"Profile\" -Passthru
$Utilisateur.Profile.PSTypeName
[/code:1]
L'accès dynamique est possible, dans ce cas le nom de la clé doit être entre guillemets
[code:1]
#Accès par une variable
$Clé1=\"EtatCivil\"
$Clé2=\"Profile\"
$Clé1,$Clé2|% {Write-Host \"`r`n
Clé : $_ ---\"; $Utilisateur.\"$_\"}
[/code:1]
Il est donc possible de construire une hashtable à la volée :
[code:1]
#On récupère le contenu d'une énumération,
#dans le bloc begin du cmdlet Foreach on initialise la hashtable,
#puis dans le bloc process on crée dans la nouvelle hashtable une entrée dont la clé est un nom de l'énumération
#et enfin on affecte à cette entrée la valeur du répertoire associé
#
#Pour utiliser comme clé le contenu d'une propriété au sein d'un pipe, faire $Utilisateur.$($_.NomPropriété)=Contenu
[System.Enum]::GetNames([Environment+SpecialFolder])|`
Foreach -begin{$SpecialFolder =@{}} -process{$SpecialFolder.$_=[System.Environment]::GetFolderPath($_)}
$SpecialFolder
# Name Value
# ----
# Favorites C:\Documents and Settings\Laurent\Favoris
# LocalApplicationData C:\Documents and Settings\Laurent\Local Settings\Application Data
# MyPictures C:\Documents and Settings\Laurent\Mes documents\Mes images
# ...
[/code:1]
Si vous souhaitez dupliquer des informations utilisez la méthode clone de l'objet ciblé car une simple affectation copiera une référence identique sur l'objet ciblé.
C'est à dire qu'une affectation recopie l'adresse de l'objet mais ne crée pas un nouvel objet.
[code:1]
$Copie=$Utilisateur.EtatCivil
$copie
Name Value
----
Adresse {, 15 rue du faubourg , Saint-Honoré, 75008 Paris}
DateNaissance 17/10/1983 18:08:01
Nom Durand
$Copie.Nom=\"Dupond\"
$Utilisateur.EtatCivil.Nom
#On modifie le même objet
#Dupond
$Clone=$Utilisateur.EtatCivil.Clone()
#On modifie un objet différent mais au contenu identique
$clone.Nom=\"Duchemin\"
$Utilisateur.EtatCivil.Nom
#Dupond
$clone.Nom
#Duchemin
[/code:1]
Il reste un petit soucis, à savoir les champs contenant d'autre structure ne sont pas clonés :
[code:1]
$Utilisateur.EtatCivil.Adresse[3]=\"78100\"
$clone
#Name Value
#----
#Adresse {, 15 rue du faubourg , Saint-Honoré, 78100}
#DateNaissance 17/10/1983 18:08:01
#Nom Duchemin
[/code:1]
Il faudrait donc mettre en place un système de copie complète ( Deep-Copy ) afin de copier tous les champs contenant un objet qui contient lui même un objet, etc :
[code:1]
$Clone.Adresse=$Utilisateur.EtatCivil.Adresse.Clone()
[/code:1]
Il existe d'autres possibilités autour de collections générique mais sous PS v1 ce n'est pas le plus aisé à utiliser, la V2 devrait améliorer ce point.
C'est d'autant plus regrettable car il existe une librairie de collections proposée par Wintellect qui offre des fonctionnalités intéressantes, liste triée, liste sans doublon, liste en lecture seule, ensemble, etc.
Un autre exemple .
[edit]
L'opération de tri peut être possible sur certaines structures, voir ce post .<br><br>Message édité par: Laurent Dardenne, à: 24/12/08 12:32
Le principe d'une hastable est d'associer une clé unique à un objet, cet objet pouvant être un type simple ,un entier une chaîne mais aussi un type plus complexe tel qu'un tableau ou une collection.
Le principe étant d'imbriquer une hashtable comme valeur d'une entrée d'une hastable. Ceci permet de créer la structure suivante :
[code:1]
#Structure 1
EtatCivil=
Nom
DateNaissance
Adresse[]
#Structure 2
Profile=
Script
Quota
#Structure 3 combinant les 2 précédentes
Utilisateur =
EtatCivil
Profile
[/code:1]
Sa mise en oeuvre se faisant de la manière suivante :
[code:1]
#Crée la Hastable.
$Utilisateur=@{}
#Crée une structure de donnée autour d'une Hastable.
#Elle référence une hastable imbriquée (EtatCivil), elle même imbriquant un tableau (Adresse)
#Notez que le type de donnée de chaque entrée n'est pas figé.
$Utilisateur.EtatCivil=@{Nom=\"\";DateNaissance=\"\";Adresse=(new-object String[] 4)}
[/code:1]
On crée un tableau en utilisant le constructeur de System.Array ce qui évite la construction : Adresse=@($null,$null,$null,$null)
[code:1]
#Idem mais avec un tableau à 2 dimension, de 2 sur 2.
#$Utilisateur.EtatCivil=@{Nom=\"\";Age=0;DateNaissance=\"\";Adresse=(new-object \"String[,]\" 2,2)}
$Utilisateur.EtatCivil.Nom=\"Durand\"
$Utilisateur.EtatCivil.DateNaissance=([DateTime]::Now).AddMonths(12*-25)
$Utilisateur.EtatCivil.Adresse[1]=\"15 rue du faubourg \"
$Utilisateur.EtatCivil.Adresse[2]=\"Saint-Honoré\"
$Utilisateur.EtatCivil.Adresse[3]=\"75008 Paris\"
#On crée une seconde hashtable
$Utilisateur.Profile=@{}
#Recherche uniquement les fichiers du répertoire courant
$Files=Dir *.*|Where {!$_.PSIsContainer}
#Dans ce cas on peut ajouter des champs au fur et à mesure
# et sans passer par une initialisation
$Utilisateur.Profile.Script=$Files[1]
$Utilisateur.Profile.Quota=200
[/code:1]
Ce qui nous donne :
[code:1]
$Utilisateur
Name Value
----
Profile {Script, Quota}
EtatCivil {Adresse, DateNaissance, Nom}
$Utilisateur.EtatCivil
Name Value
----
Adresse {, 15 rue du faubourg , Saint-Honoré, 75008 Paris}
DateNaissance 16/10/1983 19:56:44
Nom Durand
[/code:1]
Il reste possible d'ajouter des membres synthétiques à chaque structure :
[code:1]
#Le contenu est un scriptbloc calculant l'age
$Utilisateur.EtatCivil.Age={ ((Get-date) - $Utilisateur.EtatCivil.DateNaissance).TotalDays /365 -as [int]}
#Exécute le scriptbloc pour calculer l'age.
&$Utilisateur.EtatCivil.Age
#Supprime la clé Age
$Utilisateur.EtatCivil.Remove(\"Age\"«»)
# On ajoute un membre synthétique de type méthode sur l'objet référencé par la clé EtatCivil
$Utilisateur.EtatCivil=$Utilisateur.EtatCivil|Add-member ScriptMethod Age { ((Get-date) - $this.DateNaissance).TotalDays /365 -as [int]} -Passthru
$Utilisateur.EtatCivil|gm |Sort name
$Utilisateur.EtatCivil.Age()
[/code:1]
Ce dernier appel étant plus pratique que celui d'un scriptblock.
Puisque que notre objet $Utilisateur n'est pas un type personnalisé il n'est pas possible d'ajouter un nom de type dans la collection PsObject.TypeNames, on ajoute donc un membre synthétique afin de porter cette information( si besoin est):
[code:1]
$Utilisateur.Profile=$Utilisateur.Profile|Add-member NoteProperty PSTypeName \"Profile\" -Passthru
$Utilisateur.Profile.PSTypeName
[/code:1]
L'accès dynamique est possible, dans ce cas le nom de la clé doit être entre guillemets
[code:1]
#Accès par une variable
$Clé1=\"EtatCivil\"
$Clé2=\"Profile\"
$Clé1,$Clé2|% {Write-Host \"`r`n
Clé : $_ ---\"; $Utilisateur.\"$_\"}
[/code:1]
Il est donc possible de construire une hashtable à la volée :
[code:1]
#On récupère le contenu d'une énumération,
#dans le bloc begin du cmdlet Foreach on initialise la hashtable,
#puis dans le bloc process on crée dans la nouvelle hashtable une entrée dont la clé est un nom de l'énumération
#et enfin on affecte à cette entrée la valeur du répertoire associé
#
#Pour utiliser comme clé le contenu d'une propriété au sein d'un pipe, faire $Utilisateur.$($_.NomPropriété)=Contenu
[System.Enum]::GetNames([Environment+SpecialFolder])|`
Foreach -begin{$SpecialFolder =@{}} -process{$SpecialFolder.$_=[System.Environment]::GetFolderPath($_)}
$SpecialFolder
# Name Value
# ----
# Favorites C:\Documents and Settings\Laurent\Favoris
# LocalApplicationData C:\Documents and Settings\Laurent\Local Settings\Application Data
# MyPictures C:\Documents and Settings\Laurent\Mes documents\Mes images
# ...
[/code:1]
Si vous souhaitez dupliquer des informations utilisez la méthode clone de l'objet ciblé car une simple affectation copiera une référence identique sur l'objet ciblé.
C'est à dire qu'une affectation recopie l'adresse de l'objet mais ne crée pas un nouvel objet.
[code:1]
$Copie=$Utilisateur.EtatCivil
$copie
Name Value
----
Adresse {, 15 rue du faubourg , Saint-Honoré, 75008 Paris}
DateNaissance 17/10/1983 18:08:01
Nom Durand
$Copie.Nom=\"Dupond\"
$Utilisateur.EtatCivil.Nom
#On modifie le même objet
#Dupond
$Clone=$Utilisateur.EtatCivil.Clone()
#On modifie un objet différent mais au contenu identique
$clone.Nom=\"Duchemin\"
$Utilisateur.EtatCivil.Nom
#Dupond
$clone.Nom
#Duchemin
[/code:1]
Il reste un petit soucis, à savoir les champs contenant d'autre structure ne sont pas clonés :
[code:1]
$Utilisateur.EtatCivil.Adresse[3]=\"78100\"
$clone
#Name Value
#----
#Adresse {, 15 rue du faubourg , Saint-Honoré, 78100}
#DateNaissance 17/10/1983 18:08:01
#Nom Duchemin
[/code:1]
Il faudrait donc mettre en place un système de copie complète ( Deep-Copy ) afin de copier tous les champs contenant un objet qui contient lui même un objet, etc :
[code:1]
$Clone.Adresse=$Utilisateur.EtatCivil.Adresse.Clone()
[/code:1]
Il existe d'autres possibilités autour de collections générique mais sous PS v1 ce n'est pas le plus aisé à utiliser, la V2 devrait améliorer ce point.
C'est d'autant plus regrettable car il existe une librairie de collections proposée par Wintellect qui offre des fonctionnalités intéressantes, liste triée, liste sans doublon, liste en lecture seule, ensemble, etc.
Un autre exemple .
[edit]
L'opération de tri peut être possible sur certaines structures, voir ce post .<br><br>Message édité par: Laurent Dardenne, à: 24/12/08 12:32
Tutoriels PowerShell
Connexion ou Créer un compte pour participer à la conversation.
- Laurent Dardenne
- Auteur du sujet
- Hors Ligne
- Modérateur
-
Réduire
Plus d'informations
- Messages : 6311
- Remerciements reçus 68
il y a 17 ans 4 mois #3187
par Laurent Dardenne
Tutoriels PowerShell
Réponse de Laurent Dardenne sur le sujet Re:[Astuce]Création de structure de donnée.
Il reste possible de créer une hashtable ordonnée en utilisant la classe system.collections.sortedlist:
[code:1]
# msdn.microsoft.com/fr-fr/library/system....rtedlist(VS.80).aspx
Function PrintKeysAndValues( [System.Collections.SortedList] $myList ) {
\"`t-KEY-`t-VALUE-\"
for ( $i = 0; $i -lt $myList.Count; $i++ ) {
\"`t{0}:`t{1}\" -F $myList.GetKey($i), $myList.GetByIndex($i)
}
\"inverse\"
for ( $i =$myList.Count-1; $i -gt -1 ; $i-- ) {
\"`t{0}:`t{1}\" -F $myList.GetKey($i), $myList.GetByIndex($i)
}
}
$mySL = new-object System.Collections.SortedList
$mySL.Add(\"First\", \"Hello\"«»);
$mySL.Add(\"Second\", \"World\"«»);
$mySL.Add(\"Third\", \"!\"«»);
# Displays the properties and values of the SortedList.
\"mySL\"
\" Count: {0}\" -F $mySL.Count
\" Capacity: {0}\" -F $mySL.Capacity
\" Keys and Values:\"
PrintKeysAndValues $mySL
[/code:1]
A noter cette astuce de Bruce Payette autour d'une hashtable et de l'affectation multiple.
Voir aussi ce post sur la création dynamique de struct c#.<br><br>Message édité par: Laurent Dardenne, à: 5/11/08 19:35
[code:1]
# msdn.microsoft.com/fr-fr/library/system....rtedlist(VS.80).aspx
Function PrintKeysAndValues( [System.Collections.SortedList] $myList ) {
\"`t-KEY-`t-VALUE-\"
for ( $i = 0; $i -lt $myList.Count; $i++ ) {
\"`t{0}:`t{1}\" -F $myList.GetKey($i), $myList.GetByIndex($i)
}
\"inverse\"
for ( $i =$myList.Count-1; $i -gt -1 ; $i-- ) {
\"`t{0}:`t{1}\" -F $myList.GetKey($i), $myList.GetByIndex($i)
}
}
$mySL = new-object System.Collections.SortedList
$mySL.Add(\"First\", \"Hello\"«»);
$mySL.Add(\"Second\", \"World\"«»);
$mySL.Add(\"Third\", \"!\"«»);
# Displays the properties and values of the SortedList.
\"mySL\"
\" Count: {0}\" -F $mySL.Count
\" Capacity: {0}\" -F $mySL.Capacity
\" Keys and Values:\"
PrintKeysAndValues $mySL
[/code:1]
A noter cette astuce de Bruce Payette autour d'une hashtable et de l'affectation multiple.
Voir aussi ce post sur la création dynamique de struct c#.<br><br>Message édité par: Laurent Dardenne, à: 5/11/08 19:35
Tutoriels PowerShell
Connexion ou Créer un compte pour participer à la conversation.
Temps de génération de la page : 0.047 secondes
- Vous êtes ici :
-
Accueil
-
forum
-
PowerShell
-
Contributions à la communauté
- [Astuce]Création de structure de donnée.