Need guidance on creating a new object that stores user information after comparing it

I have some code that I will show below; that compares 2 objects using LINQ. There are 2 variables I have. 1 is the comparison of new items that need to be added, and the other is of items that need to be deleted.

When I do the comparison it works fine, but it compares firstnames first, then middle initial, then lastname and so forth. When I print out the variable at the very end it print it out in the same order. first names first then middle initial then last names…

What I am trying to do instead is organize it in a table that outputs it in order so for example it outputs the user and all of it’s attributes.

Here is my code.

#Our Array of values we will be comparing
[array]$CompareValues = "FirstName","MiddleName","LastName","DisplayName","Email","Mobile","TelephoneNumber","Title","Dept","Company"

for($i=0; $i -lt $CompareValues.Count; $i++)
{
    #First let's create 2 variables that will hold the info we want
    $A = ($Users).($CompareValues[$i])
    $B = ($Contacts).($CompareValues[$i])

    ##################### Compare existing contacts to Users #####################

    #Only Run if there are contacts; otherwise there is nothing for us to compare
    if(($NULL -ne $B))
    {
        #Displays what accounts we need to import
        $NeedsToBeAdded += [string[]]([Linq.Enumerable]::Except([object[]]$a, [object[]]$b))

        #Displays what accounts we need to delete because they no longer exist
        $NeedsToBeDeleted += [string[]]([Linq.Enumerable]::Except([object[]]$b, [object[]]$a))
    }
}

Here is how it prints (as an example)

Bob 
Mary
John

Jones
Joe
Nowak

Bjones
Mjoe
Jnowak

bjones@domain.com
mjoe@domain.com
jnowak@domain.com

123-456-7890
123-456-7891
123-456-7892

Manager
Accountant
Engineer

HR
Accounting
IT

CompanyName
CompanyName
CompanyName

And here is how I would like it to print as an example (no delimiter necessary I’m just using the comma for readability)

Bob , , Jones, Bjones, bjones@domain.com, 123-456-7890, , Manager, HR, CompanyName
Mary , , joe, Mjoe, mjoe@domain.com, 123-456-7891, , Accountant, Accounting, CompanyName
John , , Nowak, Jnowak, jnowak@domain.com, 123-456-7892, , Engineer, IT, CompanyName

How can I achieve something like this? My end goal is after I get it all stored in the variable correctly, I will go through each object and then update the contact list either add the new users with the attributes above, or delete the user and their contact info because they no longer exist.

Answer

First of all, you’re only updating a contact if that specific value is not in the list already, which will cause problems. For example, let’s look at last names:

$Users.LastName $Contacts.LastName
--------------- ------------------
Smith           Joe     # 
Joe-Jones       Joe     #
Smith           Nowak   ## These three changed
Smith           Smith
Joe             Joe
Nowak           Nowak

If you were to compare these by name, your script would only return one new entry. Here, two users have changed their last name to smith:

$A = ($Users).LastName
$B = ($Contacts).LastName

$NeedsToBeAdded += [string[]]([Linq.Enumerable]::Except([object[]]$a, [object[]]$b))
$NeedsToBeAdded

#Outputs:
Joe-Jones  # Missing two changes!

“Smith” was already in the contacts list, so no difference is seen.

Instead, you’ll need to compare each entire object, or have some kind of unique ID to match users and contacts on. The simplest answer for what you have is Compare-Object where you add all the properties you want to compare on:

$Diff = Compare-Object $Users $Contacts -Property FirstName,LastName,Email -PassThru

$NeedsToBeAdded   = $diff | Where SideIndicator -eq '=>'
$NeedsToBeDeleted = $diff | Where SideIndicator -eq '<='

-PassThru lets it output the a original object:

$NeedsToBeAdded | ft FirstName,LastName,Email

FirstName LastName  Email            
--------- --------  -----            
Bob       Smith     bjones@domain.com
Mary      Joe-Jones mjoe@domain.com  
John      Smith     jnowak@domain.com