Hashtables: Item has already been added

In my previous two posts about hashtables I provided examples for creating a hashtable, returning a specific value and a description around performance. This post will focus on the errors you should expect if you start using a hashtable.

The most common exception I see happens when the data I’m storing in the hashtable is not unique. For example, in the first post in the series we created a simple hashtable from a list of HTML colors. If my input list had the color ‘red’ listed on two different lines, I would have seen an error similar to this:

Add : Exception calling “Add” with “2” argument(s): “Item has already been added. Key in dictionary: ‘Red’ Key being
added: ‘Red'”
At line:1 char:17
+ $colorCodeHT.Add <<<< ('Red','123456') + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : DotNetMethodException

This is an easy error message to generate if you want to see the actual error. Follow the examples in the first post and then try and re-add the color red using the following line:


$colorCodeHT.Add('Red','123456')

The hashtable object includes a .Contains method, which can be used in an if statement to prevent this exception from happening:

if ($colorCodeHT.Contains('Red')) { "Color 'red' has already been added" } else { $colorCodeHT.Add('Red','123456') }

This statement checks for the presence of a ‘Red’ item and only attempts to add if a value does not already exist.

Posted in Scripting | Leave a comment

Hashtables: Adding, Updating and Removing Items

To help describe these concepts, I have listed a few examples based on a CSV file containing HTML color codes.

For purposes of sample data, create an array object of colors and HTML color codes.

$colorCodes = Import-Csv ColorCodes.csv

Define a hashtable object:

$colorCodeHT = @{}

Examples for adding to a hashtable:


# For the old vbscripter (this is the method I use)
# $colorCodeHT.Add('key','value')
$colorCodes | %{$colorCodeHT.Add($_.ColorName, $_.Code)}

# For the person who likes equal signs
# $colorCodeHT['key'] = 'Value'
$colorCodes | %{$colorCodeHT[$_.ColorName] = $_.Code}

Examples for updating existing entries


# Using square brackets, just like the above 'add' example
$colorCodeHT['Red'] = '#123456'

Examples for removing from a hashtable:


# Remove a single item by key
$colorCodeHT.Remove('Red')

# Remove all of the entries from the hashtable:
$colorCodeHT.Clear()
Posted in Scripting | Leave a comment

Hashtables: Performance

In the previous post I provided a quick introduction to creating and using a hashtable object. If you are paying attention, you might have asked “Why would I go to extra work to create a hashtable when I can make the original object work?” The answer is performance…but lets expand on this answer.

We can use Measure-Command to show how long each of these statements takes to execute. Here is a quick refresher on using Measure-Command to show how long it takes to execute a command…just replace “PUT EACH COMMAND HERE” with your actual command:


(Measure-Command { "PUT EACH COMMAND HERE" }).TotalMilliseconds

Using the examples from the previous post, we return Total Millisecond execution times of:

Example Execution Time (ms)
2: Select item from array object 19.0030
3: Create a hashtable from array object 19.2045
4: Select item from hashtable 0.0311

In this sample, returning a value from the hashtable (example 4) is 600 times faster than using the array object (example 2)! Just because this step was faster, it doesn’t mean hashtables are always best. If we take into account the overhead from creating the hashtable (add the execution times of example 3 and 4 together), then using the array object is actually more efficient. The performance gain is only noticeable if you plan to re-use the hashtable over several iterations.


# Example 5: Selecting multiple times from array object
(Measure-Command {
($colorCodes | Where-Object {$_.ColorName -eq 'Red'}).Code
($colorCodes | Where-Object {$_.ColorName -eq 'Blue'}).Code
($colorCodes | Where-Object {$_.ColorName -eq 'Green'}).Code
($colorCodes | Where-Object {$_.ColorName -eq 'Black'}).Code
}).TotalMilliseconds

# Example 6: Creating hashtable and selecting from it multiple times
(Measure-Command {
$colorCodeHT = @{}
$colorCodes | %{ $colorCodeHT.Add($_.ColorName, $_.Code) }
$colorCodeHT['Red']
$colorCodeHT['Blue']
$colorCodeHT['Green']
$colorCodeHT['Black']
}).TotalMilliseconds
Example Execution Time (ms)
5: Select from array object multiple times 57.7053
6: Create hashtable and select from it multiple times 17.8322

Even though a hashtable requires a bit of setup, it becomes more efficient with repetitive use. The example with HTML color codes only has around 290 items in the hashtable. This performance improvement is more noticeable with larger hashtables, especially when they are used many times within the script.

Posted in Scripting | Leave a comment

Hashtables: Introduction

I was recently working on a script for a co-worker who is trying to learn more powershell. In the script I had used a couple of hashtables to store information…which caused a couple of questions as he was reviewing the code. This post is an introduction to powershell hashtables.

A hashtable stores indexed key-value pairs in memory and is similar to the vbscript dictionary object. To help describe these concepts, I have listed a few examples based on a CSV file containing HTML color codes.

The examples below will:
1.) Import the CSV into a powershell array object,
2.) Show how to use the array object to return a specific code given a color name
3.) Convert the object from step 1 into a hashtable
4.) Show how to use the hashtable to return a specific code given a color name


# Example 1:
$colorCodes = Import-Csv ColorCodes.csv

# Example 2:
# Using just the array object created from Import-CSV
# we can select the color code for Red with just one line
($colorCodes | Where-Object {$_.ColorName -eq 'Red'}).Code

# Example 3:
# We could also create a hash table and store each item
# from the array using a couple lines of code
$colorCodeHT = @{}
$colorCodes | %{ $colorCodeHT.Add($_.ColorName, $_.Code) }

# Example 4
# And then we can retreive the code for red using either of the
# examples below. Note: you only need one as they return the same
# value.  The first sample is my preferred method, but either works.
$colorCodeHT['Red']
$colorCodeHT.Item('Red')
Posted in Scripting | Leave a comment

Using ADSI to clear an attribute

I have many scripts that write information into Active Directory. Sometimes I need to do the opposite — remove or blank out an attribute.

All of the following examples will require a distinguishedName value in AD, so I will set a common variable here. You will need a valid distinguishedName defined in a varible like this if you want to follow along.

$distinguishedName = "CN=Brian Wuchner,OU=Test Users,DC=bwuch,DC=local"

The first test we will do is to update the displayName of the user. This is easy…we don’t care what the existing value is, we are just going to set it to whatever string value we want. We will do this by creating an object representing the Directory Entry of my user, updating the displayName attribute of that object and finally updating/committing that change back to the directory as follows.


$objUser = New-Object DirectoryServices.DirectoryEntry "LDAP://$distinguishedName"
$objUser.displayName="Wuchner, Brian"
$objUser.SetInfo()

That is a rather straight forward process. However, it becomes slightly more complex if we want to clear the attribute and make it null. One would think you could simply think you could set the displayName equal to $null using something like this:


$objUser = New-Object DirectoryServices.DirectoryEntry "LDAP://$distinguishedName"
$objUser.displayName=$null

However, if you try that you will most likely get a chance to see the following error message:

Exception setting "displayName": "Value cannot be null.

As a follow up, you might think “I’ll just set the attribute to an empty string” like this:


$objUser = New-Object DirectoryServices.DirectoryEntry "LDAP://$distinguishedName"
$objUser.displayName=""
$objUser.SetInfo()

The only problem with that…setinfo will fail with the following error.

setinfo : Exception calling "setinfo" with "0" argument(s): "The attribute syntax specified to the directory service is invalid.

Does that mean its time to give up? Are we just going to have to live with bad data in AD? Never!

There are at least two ways to clean this up. The first way is my favorite (I’ve been using it since vbscript). You simply need to refer to the Microsoft article HOW TO: Use ADSI to Set LDAP Directory Attributes and you’ll realize this can be accomplished with PutEx.


$objUser = New-Object DirectoryServices.DirectoryEntry "LDAP://$distinguishedName"
$objUser.PutEx(1, 'displayName', 0)
$objUser.SetInfo()

In the above example, we use the constant ADS_PROPERTY_CLEAR by placing a ‘1’ as the first argument in our method, then specifying the displayName as the attribute we want to clear, and finishing up by providing the desired value. Since we want the value to be clear, and we’ve already specified the ‘1’ to clear, we just need to specify something so the method has enough parameters. I use 0 just for fun.

Another option is to call the Remove method and specify the value we want to remove. The downside is the attribute MUST have a value or you get an error when trying to clear it. We can overcome that with a simple if statement like this:


$objUser = New-Object DirectoryServices.DirectoryEntry "LDAP://$distinguishedName"
if ($objUser.displayName) { 
  $objUser.displayName.Remove("$($objUser.displayName)")
  $objUser.SetInfo()
}

It doesn’t matter which of the options I use, the end result is the same — a clear attribute. I hope this post helps if you ever need to clear out an attribute.

Posted in Messaging, Scripting | 2 Comments