Unique permutation in c# gives no dup but vb.net does - vb.net

So I am converting an extension method to find permutations of a generic list from c# to vb
The code in c# does not return duplicate. For example: {1,2} is same as {2,1} and {1,1} is not allowed. The vb code that I convert, however gives me a duplicate. Can someone help me spot the problem. I got the c# code from accepted answer in this thread: How to Generate Combinations of Elements of a List<T> in .NET 4.0
Here is the vb code:
Module HelperPermutation
<Extension()>
Public Function Combinations(Of T)(elements As IEnumerable(Of T), k As Integer) As IEnumerable(Of T())
Dim result As List(Of T()) = New List(Of T())
If (k = 0) Then
result.Add(New T(-1) {})
Else
Dim current As Integer = 1
For Each element In elements
result.AddRange(elements _
.Skip(current = current + 1) _
.Combinations(k - 1) _
.Select(Function(c) (New T() {element}).Concat(c).ToArray())
)
Next
End If
Return result
End Function
End module
I tried to add Distinct but it still gives me duplicates.
And this is my console app to use that extension method:
Dim list As New List(Of Integer) From {1, 2, 3}
Dim array = list.Combinations(2)
Console.WriteLine("# of permutation: " & array.Count)
For Each value In array
Console.WriteLine("-------Pairs: " & value.Count)
For i As Integer = 0 To value.Count - 1 Step 1
Console.WriteLine("value = " & value.ElementAt(i))
Next
Next
Console.ReadLine()

current = current + 1 is not doing assignment. It's an equality test, so the result of that expression is boolean. Since there is no overload for Skip() that takes a boolean, it seems like you might not be using Option Strict. I would highly suggest using it for spotting mistakes like this.
There is no post-increment built into the VB language, but luckily, you can create your own.
Function PostIncrement(ByRef arg As Integer) As Integer
arg += 1
Return arg - 1
End Function

Related

Write a .txt with values of three listboxes

I have to export a .txt file with the values of three listboxes per each row. I've tried to to this with the following code:
Using writer = New StreamWriter(SaveFileDialog1.FileName)
For Each o As String In Form10.ListBox1.Items.Cast(Of Object).Zip(Form10.ListBox2.Items.Cast(Of Object).Zip(Form6.ListBox2.Items.Cast(Of Object)), Function(x1, x2, x3) CStr(x1) & "," & CStr(x2) & "," & CStr(x3))
writer.WriteLine(o)
Next
End Using
I'm receiving several errors:
Error BC36646 Data type(s) of the type parameter(s) in extension method 'Public Function Zip(Of TSecond, TResult)(second As IEnumerable(Of TSecond), resultSelector As Func(Of Object, TSecond, TResult)) As IEnumerable(Of TResult)' defined in 'Enumerable' cannot be inferred from these arguments. Specifying the data type(s) explicitly might correct this error.
Error BC36586 Argument not specified for parameter 'resultSelector' of extension method 'Public Function Zip(Of TSecond, TResult)(second As IEnumerable(Of TSecond), resultSelector As Func(Of Object, TSecond, TResult)) As IEnumerable(Of TResult)' defined in 'Enumerable'.
Error BC42020 Variable declaration without an 'As' clause; type of Object assumed.
Unfortunately, while Form10.Listbox1 and Form10.Listbox2 are meant to be of the same size, Form6.Listbox6 is smaller; so the file I need to be exported should be with the following format:
F10.LB1,F10.LB2,F6.LB2 - for the lenght of Form6.Listbox6 and the F10.LB1,F10.LB2 for the rest of the items. If you need more explanations, please comment or answer. Thanks all are going to write me here. Best regards.
Edit :
Using writer = New StreamWriter("d:\test.txt")
For id As Integer = 0 To ListBox1.Items.Count - 1
Dim to_write As String = ListBox1.Items(id) + "," + ListBox2.Items(id)
If (ListBox3.Items.Count -1 >= id ) Then
to_write += "," + ListBox3.Items(id)
End If
writer.WriteLine(to_write)
Next
End Using
Dont forget Count -1 ;if you have 5 items : 0 to 4 = 5 items.
Are you ok with this way :
Using writer = New StreamWriter("d:\test.txt")
For Each o As String In ListBox1.Items
writer.WriteLine(o)
Next
For Each o As String In ListBox2.Items
writer.WriteLine(o)
Next
For Each o As String In ListBox3.Items
writer.WriteLine(o)
Next
End Using
Or for all listboxes :
Using writer = New StreamWriter("d:\test.txt")
For Each lbx As ListBox In Me.Controls.OfType(Of ListBox)
For Each o As String In lbx.Items
writer.WriteLine(o)
Next
Next
End Using

Arraylist.Contains Doesn't Return True VB.NET

comps.Contains doesn't return TRUE even though comps contains it.
I debugged it step by step and I can't see where the problem is.
By the way the purpose of the code is to show the pairs that sum up to SUM value. (If the sum is 5 and theres 1 and 4 then the code should return 1 and 4)
Public Function getPairs(ByVal array As ArrayList, ByVal sum As Integer)
Dim comps, pairs As New ArrayList
For index = 0 To array.Count - 1
If (comps.Contains(array(index)) = True) Then
pairs.Add(array(index))
Else
comps.Add(sum - array(index))
End If
Next
Return pairs
End Function
Sub Main()
' 1,3,2,5,46,6,7,4
' k = 5
'Dim arraylist As New ArrayList()
Console.Write("Enter your array :")
Dim arraylist As New ArrayList
arraylist.AddRange(Console.ReadLine().Split(","))
Console.Write("Enter the sum:")
Dim sum As Integer = Console.ReadLine()
getPairs(arraylist, sum)
Console.ReadKey()
End Sub
The ArrayList you populate from user input contains strings (results from splitting the user input string). The comps ArrayList contains integers (results from subtraction). When it tries to find the string "2" in the ArrayList that contains a 2, it fails.
You should convert your user input to integers so that you are comparing the same data types.
First, turn on Option Strict. Tools Menu -> Options -> Projects and Solutions -> VB Defaults. This will point out problems with your code and help you to avoid runtime errors.
ArrayList is not used much in new code but is around for backward compatibility. List(Of T) is a better choice for new code.
Module Module1
Sub Main()
' 1,3,2,5,46,6,7,4
' k = 5
'Dim arraylist As New ArrayList()
Console.Write("Enter your array :")
Dim arraylist As New ArrayList
'Option Strict would not allow this line to compile
'.Split takes a Char, the same c tells the compiler that "," is a Char
arraylist.AddRange(Console.ReadLine().Split(","c))
Console.Write("Enter the sum:")
'Option Strict would not allow a string to be dumped into an integer
Dim sum As Integer
Dim Pairs As New ArrayList
If Integer.TryParse(Console.ReadLine, sum) Then
'Your Function returns an array list but you
'throw it away by not setting up a variable to receive it
Pairs = getPairs(arraylist, sum)
Else
Console.WriteLine("Program aborted. Sum was not a number.")
End If
For Each item In Pairs
Console.WriteLine(item)
Next
Console.ReadKey()
End Sub
'Functions need a return data type in the declaration
Public Function getPairs(ByVal array As ArrayList, ByVal sum As Integer) As ArrayList
Dim comps, pairs As New ArrayList
For index = 0 To array.Count - 1
'I don't see how this can ever be true since comps is empty
If comps.Contains(array(index)) Then 'Since .Contains returns a Boolean, no = True is necessary
pairs.Add(array(index))
Else
'Ideally each item in array should be tested to see if it is a number
'You will get an exception if CInt fails
comps.Add(sum - CInt(array(index)))
'You never use the comps ArrayList
End If
Next
'The pairs ArrayList is empty
Return pairs
End Function
End Module
I don't see how this code could accomplish what you describe as your goal. I think you should start again. Talk through how you would accomplish your task. Then write it out on paper, not in code. Then you will see more clearly how to code your project.
The big problem is the original code is this line:
Dim comps, pairs As New ArrayList
That code creates two ArrayList reference variables, but only one ArrayList object. comps remains null/Nothing.
But beyond that, the ArrayList type has been dead since .Net 2.0 came out back in 2005... more than 10 years now. It only exists today for backwards compatibility with old code. Don't use it!
This is better practice, especially in conjunction with Option Strict and Option Infer:
Public Function getPairs(ByVal items As IEnumerable(Of Integer), ByVal sum As Integer) As IEnumerable(Of Integer)
Dim comps As New HashSet(Of Integer)()
Dim result As New List(Of Integer)()
For Each item As Integer In items
If Not comps.Add(item) Then
result.Add(sum - item)
End If
Next
Return result
End Function
Sub Main()
Console.Write("Enter your array: ")
Dim input As String = Console.ReadLine()
Dim list As List(Of Integer) = input.Split(",").Select(Function(item) CInt(item)).ToList()
Console.Write("Enter the sum: ")
Dim sum As Integer = CInt(Console.ReadLine())
Dim pairs = getPairs(list, sum).Select(Function(s) s.ToString())
Console.WriteLine("Pairs are: {0}", String.Join(", " pairs))
Console.ReadKey()
End Sub

Lambda function - trimming or removing on a string return type VB - Casual query

This isn't the world's most important query, actually it's a minor challenge I set myself at the end of a working day.
Basically I've converted a standard function to a Lambda but cannot quite replace the extension with a further Remove statement. So can anyone keen on Lambdas in VB explain how I can do this please?
Property Inquery As Func(Of List(Of Integer), String) = Function(x) x.Aggregate("IN(", Function(current, id) current & (id & ",")).ToString.qtrim
<Extension()>
Function qTrim(str As String)
qTrim = str.Remove(str.Length - 1, 1) & ")"
End Function
As Meta-Knight was kind enough to provide me with a great answer, I think it fair to include the version I'm now using, with the ternary if statement included for others if they want it :)
Property Inquery As Func(Of List(Of Integer), String) = Function(intList) If(intList.count > 0, "IN(" & String.Join(",", intList) & ")", "-1")
I suggest that you use the simpler String.Join method:
Property Inquery As Func(Of List(Of Integer), String) = Function(intList) "IN(" & String.Join(",", intList) & ")"
With list containing numbers 1 through 5, I get the following output:
IN(1,2,3,4,5)

VB.Net List.Find. Pass values to predicate

Having a bit of trouble using the List.Find with a custom predicate
i have a function that does this
private function test ()
Dim test As Integer = keys.Find(AddressOf FindByOldKeyAndName).NewKey
here's the function for the predicate
Private Shared Function FindByOldKeyAndName(ByVal k As KeyObj) As Boolean
If k.OldKey = currentKey.OldKey And k.KeyName = currentKey.KeyName Then
Return True
Else
Return False
End If
End Function
by doing it this way means i have to have a shared "currentKey" object in the class, and i know there has to be a way to pass in the values i'm interested in of CurrentKey (namely, keyname, and oldkey)
ideally i'd like to call it by something like
keys.Find(AddressOf FindByOldKeyAndName(Name,OldVal))
however when i do this i get compiler errors.
How do i call this method and pass in the values?
You can cleanly solve this with a lambda expression, available in VS2008 and up. A silly example:
Sub Main()
Dim lst As New List(Of Integer)
lst.Add(1)
lst.Add(2)
Dim toFind = 2
Dim found = lst.Find(Function(value As Integer) value = toFind)
Console.WriteLine(found)
Console.ReadLine()
End Sub
For earlier versions you'll have to make "currentKey" a private field of your class. Check my code in this thread for a cleaner solution.
I have an object that manages a list of Unique Property Types.
Example:
obj.AddProperty(new PropertyClass(PropertyTypeEnum.Location,value))
obj.AddProperty(new PropertyClass(PropertyTypeEnum.CallingCard,value))
obj.AddProperty(new PropertyClass(PropertyTypeEnum.CallingCard,value))
//throws exception because property of type CallingCard already exists
Here is some code to check if properties already exist
Public Sub AddProperty(ByVal prop As PropertyClass)
If Properties.Count < 50 Then
'Lets verify this property does not exist
Dim existingProperty As PropertyClass = _
Properties.Find(Function(value As PropertyClass)
Return value.PropertyType = prop.PropertyType
End Function)
'if it does not exist, add it otherwise throw exception
If existingProperty Is Nothing Then
Properties.Add(prop)
Else
Throw New DuplicatePropertyException("Duplicate Property: " + _
prop.PropertyType.ToString())
End If
End If
End Sub
I haven't needed to try this in newer versions of VB.Net which might have a nicer way, but in older versions the only way that I know of would be to have a shared member in your class to set with the value before the call.
There's various samples on the net of people creating small utility classes to wrap this up to make it a little nicer.
I've found a blog with a better "real world" context example, with good variable names.
The key bit of code to Find the object in the list is this:
' Instantiate a List(Of Invoice).
Dim invoiceList As New List(Of Invoice)
' Add some invoices to List(Of Invoice).
invoiceList.Add(New Invoice(1, DateTime.Now, 22))
invoiceList.Add(New Invoice(2, DateTime.Now.AddDays(10), 24))
invoiceList.Add(New Invoice(3, DateTime.Now.AddDays(30), 22))
invoiceList.Add(New Invoice(4, DateTime.Now.AddDays(60), 36))
' Use a Predicate(Of T) to find an invoice by its invoice number.
Dim invoiceNumber As Integer = 1
Dim foundInvoice = invoiceList.Find(Function(invoice) invoice.InvoiceNumber = invoiceNumber)
For more examples, including a date search, refer to Mike McIntyre's Blog Post

How to Generate Combinations of Elements of a List<T> in .NET 4.0

I have a question that is similar, but not identical, to the one answered here.
I would like a function generate all of the k-combinations of elements from a List of n elements. Note that I am looking for combinations, not permutations, and that we need a solution for varying k (i.e., hard-coding the loops is a no-no).
I am looking for a solution that is a) elegant, and b) can be coded in VB10/.Net 4.0.
This means a) solutions requiring LINQ are ok, b) those using the C# "yield" command are not.
The order of the combinations is not important (i.e., lexicographical, Gray-code, what-have-you) and elegance is favored over performance, if the two are in conflict.
(The OCaml and C# solutions here would be perfect, if they could be coded in VB10.)
Code in C# that produces list of combinations as arrays of k elements:
public static class ListExtensions
{
public static IEnumerable<T[]> Combinations<T>(this IEnumerable<T> elements, int k)
{
List<T[]> result = new List<T[]>();
if (k == 0)
{
// single combination: empty set
result.Add(new T[0]);
}
else
{
int current = 1;
foreach (T element in elements)
{
// combine each element with (k - 1)-combinations of subsequent elements
result.AddRange(elements
.Skip(current++)
.Combinations(k - 1)
.Select(combination => (new T[] { element }).Concat(combination).ToArray())
);
}
}
return result;
}
}
Collection initializer syntax used here is available in VB 2010 (source).
I tried creating an enumerable that can accomplish this task in VB. This is the result:
Public Class CombinationEnumerable(Of T)
Implements IEnumerable(Of List(Of T))
Private m_Enumerator As CombinationEnumerator
Public Sub New(ByVal values As List(Of T), ByVal length As Integer)
m_Enumerator = New CombinationEnumerator(values, length)
End Sub
Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of List(Of T)) Implements System.Collections.Generic.IEnumerable(Of List(Of T)).GetEnumerator
Return m_Enumerator
End Function
Private Function GetEnumerator1() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
Return m_Enumerator
End Function
Private Class CombinationEnumerator
Implements IEnumerator(Of List(Of T))
Private ReadOnly m_List As List(Of T)
Private ReadOnly m_Length As Integer
''//The positions that form the current combination
Private m_Positions As List(Of Integer)
''//The index in m_Positions that we are currently moving
Private m_CurrentIndex As Integer
Private m_Finished As Boolean
Public Sub New(ByVal list As List(Of T), ByVal length As Integer)
m_List = New List(Of T)(list)
m_Length = length
End Sub
Public ReadOnly Property Current() As List(Of T) Implements System.Collections.Generic.IEnumerator(Of List(Of T)).Current
Get
If m_Finished Then
Return Nothing
End If
Dim combination As New List(Of T)
For Each position In m_Positions
combination.Add(m_List(position))
Next
Return combination
End Get
End Property
Private ReadOnly Property Current1() As Object Implements System.Collections.IEnumerator.Current
Get
Return Me.Current
End Get
End Property
Public Function MoveNext() As Boolean Implements System.Collections.IEnumerator.MoveNext
If m_Positions Is Nothing Then
Reset()
Return True
End If
While m_CurrentIndex > -1 AndAlso (Not IsFree(m_Positions(m_CurrentIndex) + 1)) _
''//Decrement index of the position we're moving
m_CurrentIndex -= 1
End While
If m_CurrentIndex = -1 Then
''//We have finished
m_Finished = True
Return False
End If
''//Increment the position of the last index that we can move
m_Positions(m_CurrentIndex) += 1
''//Add next positions just after it
Dim newPosition As Integer = m_Positions(m_CurrentIndex) + 1
For i As Integer = m_CurrentIndex + 1 To m_Positions.Count - 1
m_Positions(i) = newPosition
newPosition += 1
Next
m_CurrentIndex = m_Positions.Count - 1
Return True
End Function
Public Sub Reset() Implements System.Collections.IEnumerator.Reset
m_Finished = False
m_Positions = New List(Of Integer)
For i As Integer = 0 To m_Length - 1
m_Positions.Add(i)
Next
m_CurrentIndex = m_Length - 1
End Sub
Private Function IsFree(ByVal position As Integer) As Boolean
If position < 0 OrElse position >= m_List.Count Then
Return False
End If
Return Not m_Positions.Contains(position)
End Function
''//Add IDisposable support here
End Class
End Class
...and you can use my code this way:
Dim list As New List(Of Integer)(...)
Dim iterator As New CombinationEnumerable(Of Integer)(list, 3)
For Each combination In iterator
Console.WriteLine(String.Join(", ", combination.Select(Function(el) el.ToString).ToArray))
Next
My code gives combinations of a specified length (3 in my example) though, I just realized that you wish to have combinations of any length (I think), but it's a good start.
It's not clear to me in what form you want your VB code to return the combinations it generates, but for simplicity let's assume a list of lists. VB does allow recursion, and a recursive solution is simplest. Doing combinations rather than permutations can be obtained easily, by simply respecting the ordering of the input list.
So, the combinations of K items out of a list L that's N items long are:
none, if K > N
the whole list L, if K == N
if K < N, then the union of two bunches: those that contain the first item of L and any of the combinations of K-1 of the other N-1 items; plus, the combinations of K of the other N-1 items.
In pseudocode (using for example .size to give a list's length, [] as an empty list, .append to add an item to a list, .head to get a list's first item, .tail to get the list of "all but the first" items of L):
function combinations(K, L):
if K > L.size: return []
else if K == L.size:
result = []
result.append L
return result
else:
result = []
for each sublist in combinations(K-1, L.tail):
subresult = []
subresult.append L.head
for each item in sublist:
subresult.append item
result.append subresult
for each sublist in combinations(K, L.tail):
result.append sublist
return result
This pseudocode can be made more concise if you assume more flexible list-manipulation syntax. For example, in Python ("executable pseudocode") using "slicing" and "list comprehension" syntax:
def combinations(K, L):
if K > len(L): return []
elif K == len(L): return [L]
else: return [L[:1] + s for s in combinations(K-1, L[1:])
] + combinations(K, L[1:])
Whether you need to verbosely construct lists by repeated .append, or can concisely construct them by list comprehension notation, is a syntax detail (as is the choice of head and tail vs list slicing notation to get the first item of the list vs the rest): the pseudocode is intended to express exactly the same idea (which is also the same idea expressed in English in the previous numbered list). You can implement the idea in any language that is capable of recursion (and, of course, some minimal list operations!-).
My twist, delivering a sorted list, first by length - then by alpha
Imports System.Collections.Generic
Public Class LettersList
Public Function GetList(ByVal aString As String) As List(Of String)
Dim returnList As New List(Of String)
' Start the recursive method
GetListofLetters(aString, returnList)
' Sort the list, first by length, second by alpha
returnList.Sort(New ListSorter)
Return returnList
End Function
Private Sub GetListofLetters(ByVal aString As String, ByVal aList As List(Of String))
' Alphabetize the word, to make letter key
Dim tempString As String = Alphabetize(aString)
' If the key isn't blank and the list doesn't already have the key, add it
If Not (String.IsNullOrEmpty(tempString)) AndAlso Not (aList.Contains(tempString)) Then
aList.Add(tempString)
End If
' Tear off a letter then recursify it
For i As Integer = 0 To tempString.Length - 1
GetListofLetters(tempString.Remove(i, 1), aList)
Next
End Sub
Private Function Alphabetize(ByVal aString As String) As String
' Turn into a CharArray and then sort it
Dim aCharArray As Char() = aString.ToCharArray()
Array.Sort(aCharArray)
Return New String(aCharArray)
End Function
End Class
Public Class ListSorter
Implements IComparer(Of String)
Public Function Compare(ByVal x As String, ByVal y As String) As Integer Implements System.Collections.Generic.IComparer(Of String).Compare
If x.Length = y.Length Then
Return String.Compare(x, y)
Else
Return (x.Length - y.Length)
End If
End Function
End Class
I can offer the following solution - not yet perfect, not fast, and it assumes the input is a set, hence contains no duplicate items. I am going to add some explanation later.
using System;
using System.Linq;
using System.Collections.Generic;
class Program
{
static void Main()
{
Int32 n = 5;
Int32 k = 3;
Boolean[] falseTrue = new[] { false, true };
Boolean[] pattern = Enumerable.Range(0, n).Select(i => i < k).ToArray();
Int32[] items = Enumerable.Range(1, n).ToArray();
do
{
Int32[] combination = items.Where((e, i) => pattern[i]).ToArray();
String[] stringItems = combination.Select(e => e.ToString()).ToArray();
Console.WriteLine(String.Join(" ", stringItems));
var right = pattern.SkipWhile(f => !f).SkipWhile(f => f).Skip(1);
var left = pattern.Take(n - right.Count() - 1).Reverse().Skip(1);
pattern = left.Concat(falseTrue).Concat(right).ToArray();
}
while (pattern.Count(f => f) == k);
Console.ReadLine();
}
}
It generates a sequence of boolean patterns that determine if an element belongs to the current combination starting with k times true (1) at the very left and the rest all false (0).
n = 5 k = 3
11100
11010
10110
01110
11001
10101
01101
10011
01011
00100
The next pattern is generated as follows. Assume the current pattern is the following.
00011110000110.....
Scan from left to right and skip all zeros (false).
000|11110000110....
Scan further over the first block of ones (true).
0001111|0000110....
Move all the skipped ones besides the rightmost one back to the very left.
1110001|0000110...
And finally move the rightmost skipped one a single position to the right.
1110000|1000110...