65.9K
CodeProject is changing. Read more.
Home

Permutations of Dictionary Items

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (3 votes)

Sep 23, 2013

CPOL

1 min read

viewsIcon

14093

downloadIcon

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:

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:

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:

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:

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.