ComboBox FindString Contains - vb.net

I have such entries in Winforms ComboBox:
Font 8 pt
Font 9 pt
Font 10 pt
Font 12 pt
Font 14 pt
Then I have search string " 9 ".
Is here native way to find index by search string without looping?
I try this:
Dim a As Integer = myComboBox.FindString(" 9 ", 0)
... but without good result.

First, no, there is no available method in the framework that searches for sub-string in combobox items and returns the index of the first item which contains the search parameter.
But even ComboBox.FindString uses a loop as you can see in the source.
So there is nothing bad in using one, you could write an extension method for this:
public static class ControlExtensions
{
public static int FindSubStringIndex(this ComboBox combo, string subString, StringComparison comparer = StringComparison.CurrentCulture)
{
// Sanity check parameters
if(combo == null) throw new ArgumentNullException("combo");
if (subString == null) {
return -1;
}
for (int index = 0; index < combo.Items.Count; index++)
{
object obj = combo.Items[index];
if(obj == null) continue;
string item = Convert.ToString(obj, CultureInfo.CurrentCulture);
if (string.IsNullOrWhiteSpace(item) && string.IsNullOrWhiteSpace(subString))
return index;
int indexInItem = item.IndexOf(subString, comparer);
if (indexInItem >= 0)
return index;
}
return -1;
}
}
Now you can use it in this way:
int index = combo.FindSubStringIndex("9");
Whoops, VB.NET:
Public Module ControlExtensions
<System.Runtime.CompilerServices.Extension> _
Public Function FindSubStringIndex(combo As ComboBox, subString As String, Optional comparer As StringComparison = StringComparison.CurrentCulture) As Integer
' Sanity check parameters
If combo Is Nothing Then
Throw New ArgumentNullException("combo")
End If
If subString Is Nothing Then
Return -1
End If
For index As Integer = 0 To combo.Items.Count - 1
Dim obj As Object = combo.Items(index)
If obj Is Nothing Then
Continue For
End If
Dim item As String = Convert.ToString(obj, CultureInfo.CurrentCulture)
If String.IsNullOrWhiteSpace(item) AndAlso String.IsNullOrWhiteSpace(subString) Then
Return index
End If
Dim indexInItem As Integer = item.IndexOf(subString, comparer)
If indexInItem >= 0 Then
Return index
End If
Next
Return -1
End Function
End Module

Related

How do I convert type _ComObject to a native type like Long or other (getting cast error)?

I am trying to get the LastLogonTimestamp from Active Directory by calling
Principal.ExtensionGet("lastLogonTimestamp")
VB.NET code:
<DirectoryProperty("lastLogonTimestamp")>
Public Property LastLogonTimestamp() As Date? ' no matter what this type is, I cannot cast the Object coming in
Get
Dim valueArray = ExtensionGet("lastLogonTimestamp")
If valueArray Is Nothing OrElse valueArray.Length = 0 Then Return Nothing
Return DateTime.FromFileTimeUtc(valueArray(0))
End Get
Set(value As Date?)
ExtensionSet("lastLogonTimestamp", value)
End Set
End Property
This returns an array of Object (i.e. Object()) or null. The trouble is it complains about my cast to Long (or other types I have tried like: ULong, Date, and String). It always tells me something like this:
Conversion from type '_ComObject' to type 'Long' is not valid.
In a new question, I set out to go the other way (from DateTime to 64 bit)
Using C# code provided in link via HansPassant's comment below, I resolved this with the following VB code:
<DirectoryProperty("lastLogonTimestamp")>
Public Property LastLogonTimestamp() As Date?
Get
'Dim valueArray = GetProperty("whenChanged")
Dim valueArray = ExtensionGet("lastLogonTimestamp") 'ExtensionGet("LastLogon")
If valueArray Is Nothing OrElse valueArray.Length = 0 Then Return Nothing
Dim lastLogonDate = valueArray(0)
Dim lastLogonDateType = lastLogonDate.GetType()
Dim highPart = CType(lastLogonDateType.InvokeMember("HighPart", Reflection.BindingFlags.GetProperty, Nothing, lastLogonDate, Nothing), Int32)
Dim lowPart = CType(lastLogonDateType.InvokeMember("LowPart", Reflection.BindingFlags.GetProperty Or Reflection.BindingFlags.Public, Nothing, lastLogonDate, Nothing), Int32)
Dim longDate = CLng(highPart) << 32 Or (CLng(lowPart) And &HFFFFFFFFL)
Dim result = IIf(longDate > 0, CType(DateTime.FromFileTime(longDate), DateTime?), Nothing)
Return result
'Return DateTime.FromFileTimeUtc(valueArray(0))
End Get
Set(value As Date?)
ExtensionSet("lastLogonTimestamp", value)
End Set
End Property
And the C# version (clipped from source):
[DirectoryProperty("RealLastLogon")]
public DateTime? RealLastLogon
{
get
{
if (ExtensionGet("LastLogon").Length > 0)
{
var lastLogonDate = ExtensionGet("LastLogon")[0];
var lastLogonDateType = lastLogonDate.GetType();
var highPart = (Int32)lastLogonDateType.InvokeMember("HighPart", BindingFlags.GetProperty, null, lastLogonDate, null);
var lowPart = (Int32)lastLogonDateType.InvokeMember("LowPart", BindingFlags.GetProperty | BindingFlags.Public, null, lastLogonDate, null);
var longDate = ((Int64)highPart << 32 | (UInt32)lowPart);
return longDate > 0 ? (DateTime?) DateTime.FromFileTime(longDate) : null;
}
return null;
}
}

Sort List(of Object) by object properties

I'm trying to achieve something where the answer is already given for. But it's in c# and I don't have any knowledge what-so-ever over c# so I'm looking for a vb.net alternative.
I made a class called BomItem which has several properties like quantity, description etc.
I add these BomItems into a List(of BomItem) but now I would like to sort them according to a property. How can you sort the items based on the ItemNumber property?
Here is the link to the c# solution I found.
My class code
Public Class BomItem
Public Property ItemNumber As String
Public Property Description As String
Public Property Quantity As Double
Public Property Material As String
Public Property Certificate As String
End Class
How I add the BomRow objects
_NewBomList.Add(New BomItem() With {
.ItemNumber = oRow.ItemNumber,
.Description = oPropSet.Item("Description").Value,
.Quantity = oRow.TotalQuantity,
.Material = oPropSet.Item("Material").Value,
.Certificate = CustomPropertySet.Item("Cert.").Value})
Comparer
Public Class NaturalSort
Implements IComparer
Public Function Compare(ByVal x As Object,
ByVal y As Object) As Integer Implements IComparer.Compare
' [1] Validate the arguments.
Dim s1 As String = x
If s1 = Nothing Then
Return 0
End If
Dim s2 As String = y
If s2 = Nothing Then
Return 0
End If
Dim len1 As Integer = s1.Length
Dim len2 As Integer = s2.Length
Dim marker1 As Integer = 0
Dim marker2 As Integer = 0
' [2] Loop over both Strings.
While marker1 < len1 And marker2 < len2
' [3] Get Chars.
Dim ch1 As Char = s1(marker1)
Dim ch2 As Char = s2(marker2)
Dim space1(len1) As Char
Dim loc1 As Integer = 0
Dim space2(len2) As Char
Dim loc2 As Integer = 0
' [4] Collect digits for String one.
Do
space1(loc1) = ch1
loc1 += 1
marker1 += 1
If marker1 < len1 Then
ch1 = s1(marker1)
Else
Exit Do
End If
Loop While Char.IsDigit(ch1) = Char.IsDigit(space1(0))
' [5] Collect digits for String two.
Do
space2(loc2) = ch2
loc2 += 1
marker2 += 1
If marker2 < len2 Then
ch2 = s2(marker2)
Else
Exit Do
End If
Loop While Char.IsDigit(ch2) = Char.IsDigit(space2(0))
' [6] Convert to Strings.
Dim str1 = New String(space1)
Dim str2 = New String(space2)
' [7] Parse Strings into Integers.
Dim result As Integer
If Char.IsDigit(space1(0)) And Char.IsDigit(space2(0)) Then
Dim thisNumericChunk = Integer.Parse(str1)
Dim thatNumericChunk = Integer.Parse(str2)
result = thisNumericChunk.CompareTo(thatNumericChunk)
Else
result = str1.CompareTo(str2)
End If
' [8] Return result if not equal.
If Not result = 0 Then
Return result
End If
End While
' [9] Compare lengths.
Return len1 - len2
End Function
End Class
Use LINQ OrderBy:
_NewBomList.OrderBy(Function(bi) bi.ItemNumber)
and for descending:
_NewBomList.OrderByDescending(Function(bi) bi.ItemNumber)
If you want a numeric order in your string you have to convert it to an integer first:
_NewBomList.OrderBy(Function(bi) Integer.Parse(bi.ItemNumber))
Edit:
To provide a custom IComparer for the OrderBy extension you have to create a class which implements IComparer(Of String) where String are your ItemNumbers to compare:
Class ItemNumberComparer
Implements IComparer(Of String)
Public Function Compare(String x, String y)
Dim ix As String() = x.Split("."C)
Dim iy As String() = y.Split("."C)
Dim maxLen As Integer = Math.Max(ix.Length, iy.Length)
For i As Integer = 0 To maxLen - 2
If ix.Length >= i AndAlso iy.Length >= i Then
If Integer.Parse(ix(i)) < Integer.Parse(iy(i)) Then
Return -1 'If x.i is LT y.i it must be smaller at all
ElseIf Integer.Parse(ix(i)) > Integer.Parse(iy(i)) Then
Return 1 'If x.i is GT y.i it must be bigger all
End If
End If
Next
'This code is only executed if x and y differ at last number or have different ´number of dots
If ix.Length = iy.Length Then
Return Integer.Parse(ix(ix.Length - 1)).CompareTo(Integer.Parse(iy(iy.Length - 1))) 'Only last number differs
Else
Return ix.Length.CompareTo(iy.Length) 'The number with more dots is smaller
End If
End Function
End Class
Call syntax:
Dim comparer = new ItemNumberComparer()
_NewBomList.OrderByDescending(Function(bi) bi.ItemNumber, comparer)
This C# code from that other thread:
List<Order> SortedList = objListOrder.OrderBy(o=>o.OrderDate).ToList();
equates to this VB code:
List(Of Order) SortedList = objListOrder.OrderBy(Function(o) o.OrderDate).ToList()
As you can see, very little changes. You just have to know the syntax for generics and lambda expressions.
You should be aware, though, that this code does NOT sort your list. It sorts the items in the list and then adds them to a new list in that order. This is perfectly fine for many applications but if you're using that same list elsewhere then you won't see the new order there. While there are a few variations, one way to actually sort the list in place is like this:
objListOrder.Sort(Function(o1, o2) o1.OrderDate.CompareTo(o2.OrderDate))
Another solution would be to implement the IComparable (see MSDN ref) interface. This interface is designed to sort your objects on a custom way :
Public Class BomItem
Implements IComparable
Public Property ItemNumber As String
Public Property Description As String
Public Property Quantity As Double
Public Property Material As String
Public Property Certificate As String
Public Function CompareTo(obj As Object) As Integer
Dim bom = CType(obj, BomItem)
If Not bom Is Nothing then
Return Me.ItemNumber.CompareTo(bom.ItemNumber)
Else
Throw New ArgumentException("Object is not a BomItem")
End If
End Class
and you can sort the list this way :
Dim myList As New List(Of BomItem)
'Code to add your BomItems
myList.Sort()
This will actually sort your list, it does not create a new list.

VB.NET - Extracting text inside quotation in a complex string

I have a string looking like this:
a:391:i:0;s:12:"jnKKPkvpNnfn";i:1;s:12:"ic9VAk3PvQ3j";i:2;s:12:"PEBFuE6bGepr";i:3;s:12:"bwuxRkH6QbGp";i:4;s:12:"LSRDQbAKXc9q";i:5;s:12:"eLuVbSAxQCgo";}
And I want to get the text inside the quotations and send them to a listbox.
I know sort of how to do it, but in an ineffective way that might now work... So I'm asking for advice with an example on how to do it.
Thanks
This should get you started; the method will run through an input string and return an array of strings that are contained within quotes.
string[] ParseQuotes(string input)
{
List<string> matches = new List<string>();
bool open = false;
int index = -1;
for (int i = 0; i < input.Length; i++)
{
if (input[i] == '"')
{
if (!open)
{
open = true;
index = i;
}
else
{
open = false;
string match = input.Substring(index + 1, index - i - 1);
matches.Add(match);
}
}
}
return matches.ToArray();
}
Converted to VB...
Private Function ParseQuotes(input As String) As String()
Dim matches As New List(Of String)()
Dim open As Boolean = False
Dim index As Integer = -1
For i As Integer = 0 To input.Length - 1
If input(i) = """"C Then
If Not open Then
open = True
index = i
Else
open = False
Dim match As String = input.Substring(index + 1, index - i - 1)
matches.Add(match)
End If
End If
Next
Return matches.ToArray()
End Function
In your main code:
cbYourcomboBox.Items.Clear()
cbYourcomboBox.Items.AddRange(GetList(str).ToArray)
and then the function itself:
Public Function GetList(ByVal str As String) As List(Of String)
Dim ar As String()
Dim ar2 As List(Of String) = New List(Of String)
ar = Split(str, Chr(34))
' making sure there is a matching closing quote with - (UBound(ar) And 1)
For a As Integer = 1 To UBound(ar) - (UBound(ar) And 1) Step 2
ar2.Add(ar(a))
Next a
Return ar2
End Function

how to sort list with strings containing numbers?

I have a Tlist that contains addresses. When I sort the list, numbers in the address are considered a string, and it doesn't sort correctly. How should I sort the list ascending?
Dim sortme As List(Of data) = tempdata 'main list containing addresses as strings.
sortme.Sort(Function(p1 As data, p2 As data) numorder(p1.Value).CompareTo(numorder(p2.Value)))
Private Function numorder(ByVal str As String)
Try
Dim words() As String = str.Split(" "c) 'read string up to first space (for number)
Return Convert.ToInt32(words(0))
Catch ex As Exception
End Try
End Function
Example of current output:
1001 street name
103 street name
1021 street name
It should be:
103 street name
1001 street name
1021 street name
The idea is to write your own comparer which will firs consider the number prefix and then the string itself. This comparer can be then used anywhere, for instance in LINQ OrderBy(). Here an example in c# see full VB.NET version below.
public class StreetComparer : IComparer<string>
{
public int Compare(string x, string y)
{
int indexOfSpaceX = x.IndexOf(' ');
string numericalPartX = x.Substring(0, indexOfSpaceX);
int indexOfSpaceY = y.IndexOf(' ');
string numericalPartY = y.Substring(0, indexOfSpaceY);
int numberX;
int numberY;
if(!int.TryParse(numericalPartX, out numberX) ||
!int.TryParse(numericalPartY, out numberY))
{
//Some code to handle the case where number is missing
throw new ArgumentException();
}
if (numberX!=numberY)
{
return numberX-numberY;
}
string textPartX = x.Substring(indexOfSpaceX + 1);
string textPartY = x.Substring(indexOfSpaceY + 1);
return String.Compare(textPartX, textPartY, true, CultureInfo.CurrentCulture);
}
}
class Program
{
static void Main(string[] args)
{
var myStreets = new[] {"01 aaa", "02 bbb"};
var result = myStreets.OrderBy(s => s, new StreetComparer());
}
}
Now a VB.NET version adapted exactly to your use case a List with classes sorted by property:
Public Class StreetComparer
Implements IComparer(Of String)
Public Function Compare(x As String, y As String) As Integer
Dim indexOfSpaceX As Integer = x.IndexOf(" "C)
Dim numericalPartX As String = x.Substring(0, indexOfSpaceX)
Dim indexOfSpaceY As Integer = y.IndexOf(" "C)
Dim numericalPartY As String = y.Substring(0, indexOfSpaceY)
Dim numberX As Integer
Dim numberY As Integer
If Not Integer.TryParse(numericalPartX, numberX) OrElse Not Integer.TryParse(numericalPartY, numberY) Then
'Some code to handle the case where number is missing
Throw New ArgumentException()
End If
If numberX <> numberY Then
Return numberX - numberY
End If
Dim textPartX As String = x.Substring(indexOfSpaceX + 1)
Dim textPartY As String = x.Substring(indexOfSpaceY + 1)
Return [String].Compare(textPartX, textPartY, True, CultureInfo.CurrentCulture)
End Function
End Class
Public Class Person
Public Property Value() As String
Get
Return m_Value
End Get
Set
m_Value = Value
End Set
End Property
Private m_Value As String
Public Sub New(value__1 As String)
Value = value__1
End Sub
End Class
Class Program
Private Shared Sub Main(args As String())
Dim sortme As New List(Of Person)(New () {New Person("1001 street name"), New Person("103 street name"), New Person("1021 street name")})
Dim result = sortme.OrderBy(Function(p) p.Value, New StreetComparer())
For Each person As var In result
Console.WriteLine(person.Value)
Next
Console.ReadKey()
End Sub
End Class

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...