Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

Permutations of Dictionary Items

4.71/5 (3 votes)
3 Jul 2015CPOL1 min read 14.1K   144  
Generate every permutation of dictionary items.

Introduction

In this tip, I will present an easy to use method for iterating over all permutations of a model. Each model parameter is defined as an entry into a Dictionary(of String, Object). Any number of model parameters can be used as input and the output is a Dictionary(of Integer, Object) where the integer key is the permutation number and the object is another dictionary that contains the model parameters for that permutation. This is useful in providing every possible set of inputs into a function.

Background

Other permutation examples will give you every instance of a string or integer array. What if your inputs are more complex? By using a Dictionary(of String, Object) for model inputs, a name for the parameter can be defined and the parameters can be anything from a simple array, true/false values, a class, or a structure.

Using the Code

Examples here are in VB. Download for the C# project for C# code.

Use the code by defining a Dictionary(of String, Object) inputs:

VB.NET
Dim d As New Dictionary(Of String, Object)

Dim b() As Boolean = {True, False}
d.Add("IsMarried", b)
d.Add("IsEmployed", b)

d.Add("YearsAtAddress", {2, 5, 10})
d.Add("IncomeLevel", {25000, 50000, 75000, 100000})

Dim cb As New List(Of Credit)
cb.Add(New Credit With {.Bureau = "Equifax", .ScoreAvg = 500})
cb.Add(New Credit With {.Bureau = "Equifax", .ScoreAvg = 600})
cb.Add(New Credit With {.Bureau = "Equifax", .ScoreAvg = 700})
cb.Add(New Credit With {.Bureau = "Transunion", .ScoreAvg = 500})
cb.Add(New Credit With {.Bureau = "Transunion", .ScoreAvg = 600})
cb.Add(New Credit With {.Bureau = "Transunion", .ScoreAvg = 700})
d.Add("Credit", cb) 

Then call the function:

VB.NET
Dim ps As Dictionary(Of Integer, Object) = PermutateDictionary.Permutations(d) 

The result is a dictionary containing all the permutations and the parameters for each.

1: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 25000(IncomeLevel) Equifax-500(Credit)
2: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 25000(IncomeLevel) Equifax-600(Credit)
3: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 25000(IncomeLevel) Equifax-700(Credit)
4: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 25000(IncomeLevel) Transunion-500(Credit)
5: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 25000(IncomeLevel) Transunion-600(Credit)
6: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 25000(IncomeLevel) Transunion-700(Credit)
7: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 50000(IncomeLevel) Equifax-500(Credit)
8: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 50000(IncomeLevel) Equifax-600(Credit)
9: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 50000(IncomeLevel) Equifax-700(Credit)
10: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 50000(IncomeLevel) Transunion-500(Credit)
11: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 50000(IncomeLevel) Transunion-600(Credit)
12: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 50000(IncomeLevel) Transunion-700(Credit)
13: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 75000(IncomeLevel) Equifax-500(Credit)
14: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 75000(IncomeLevel) Equifax-600(Credit)
15: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 75000(IncomeLevel) Equifax-700(Credit)
16: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 75000(IncomeLevel) Transunion-500(Credit)
17: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 75000(IncomeLevel) Transunion-600(Credit)
18: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 75000(IncomeLevel) Transunion-700(Credit)
19: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 100000(IncomeLevel) Equifax-500(Credit)
20: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 100000(IncomeLevel) Equifax-600(Credit)
21: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 100000(IncomeLevel) Equifax-700(Credit)
22: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 100000(IncomeLevel) Transunion-500(Credit)
23: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 100000(IncomeLevel) Transunion-600(Credit)
24: True(IsMarried) True(IsEmployed) 2(YearsAtAddress) 100000(IncomeLevel) Transunion-700(Credit)
.
.
.
281: False(IsMarried) False(IsEmployed) 10(YearsAtAddress) 75000(IncomeLevel) Transunion-600(Credit)
282: False(IsMarried) False(IsEmployed) 10(YearsAtAddress) 75000(IncomeLevel) Transunion-700(Credit)
283: False(IsMarried) False(IsEmployed) 10(YearsAtAddress) 100000(IncomeLevel) Equifax-500(Credit)
284: False(IsMarried) False(IsEmployed) 10(YearsAtAddress) 100000(IncomeLevel) Equifax-600(Credit)
285: False(IsMarried) False(IsEmployed) 10(YearsAtAddress) 100000(IncomeLevel) Equifax-700(Credit)
286: False(IsMarried) False(IsEmployed) 10(YearsAtAddress) 100000(IncomeLevel) Transunion-500(Credit)
287: False(IsMarried) False(IsEmployed) 10(YearsAtAddress) 100000(IncomeLevel) Transunion-600(Credit)
288: False(IsMarried) False(IsEmployed) 10(YearsAtAddress) 100000(IncomeLevel) Transunion-700(Credit) 

The code to produce the output is:

VB.NET
For Each p In ps
    Console.Write(CStr(p.Key) + ": ")
    For Each e In p.Value
        If TypeName(e.value) = "Credit" Then
            Console.Write(CStr(e.value.Bureau + "-" + _
                          e.value.ScoreAvg.ToString) + "(" + e.key + ") ")
        Else
            Console.Write(CStr(e.value) + "(" + e.key + ") ")
        End If
    Next
    Console.WriteLine()
Next

The code that does the work is:

VB.NET
Public Class PermutateDictionary

    Shared Function Permutations(ds As Dictionary(Of String, Object)) _
        As Dictionary(Of Integer, Object)

        Dim pCount As Integer = 1
        Dim sPointer As New Dictionary(Of String, Integer)
        Dim pNumber As Integer
        Dim result As New Dictionary(Of Integer, Object)

        For Each d In ds

            pCount *= GetEntryCount(d)
            sPointer.Add(d.Key, 0)
        Next

        For pNumber = 1 To pCount

            Dim pEntry As New Dictionary(Of String, Object)
            For Each d In ds
                pEntry.Add(d.Key, d.Value(sPointer(d.Key)))
            Next
            result.Add(pNumber, pEntry)

            'Iterate and increment the pointers
            For Each d In ds.Reverse
                sPointer(d.Key) += 1
                If sPointer(d.Key) = GetEntryCount(d) Then
                    sPointer(d.Key) = 0
                Else
                    Exit For
                End If
            Next
        Next
        Return result

    End Function

    Private Shared Function GetEntryCount(ByRef Entry As Object) _
        As Integer

        Try
            Return Entry.Value.length
        Catch ex As Exception
            Return Entry.Value.count
        End Try
    End Function
End Class 

Download the sample project. I hope you find this useful.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)