Sorting a List based on an ArrayList within a custom Object - vb.net

I am using a list to keep track of a number of custom Row objects as follows:
Public Rows As List(Of Row)()
Row has 2 properties, Key (a String) and Cells (an ArrayList).
I need to be able to sort each Row within Rows based on a developer defined index of the Cells ArrayList.
So for example based on the following Rows
Row1.Cells = ("b", "12")
Row2.Cells = ("a", "23")
Rows.Sort(0) would result in Row2 being first in the Rows list. What would be the best way of going about implementing this?
Thanks

If you implement IComparable on your Row object, you can define a custom comparison process to be able to do the sorting that you desire.
Here is an intro article to get you started.

Its similar to a DatRow with it's Columns, so you can alternative(to the IComparable solution) use the sort-ability of a DataView:
Consider creating a DataTabale on the fly with its columns as your cells.
For example:
Dim rows As New List(Of Row)
Dim row = New Row("Row 1")
Dim cell1 As New Row.Cell("b")
Dim cell2 As New Row.Cell("12")
row.Cells.Add(cell1)
row.Cells.Add(cell2)
rows.Add(row)
row = New Row("Row 2")
cell1 = New Row.Cell("a")
cell2 = New Row.Cell("23")
row.Cells.Add(cell1)
row.Cells.Add(cell2)
rows.Add(row)
Dim tbl As New DataTable()
''#are the cell count in every row equal? Otherwise you can calculate the max-count
Dim cellCount As Int32 = 2 ''#for example
For i As Int32 = 0 To cellCount - 1
Dim newCol As New DataColumn("Column " & i + 1)
tbl.Columns.Add(newCol)
Next
For rowIndex As Int32 = 0 To rows.Count - 1
row = rows(rowIndex)
Dim newRow As DataRow = tbl.NewRow
For cellIndex As Int32 = 0 To row.Cells.Count - 1
Dim cell As Row.Cell = row.Cells(cellIndex)
newRow(cellIndex) = cell.value
Next
tbl.Rows.Add(newRow)
Next
Dim view As New DataView(tbl)
view.Sort = "Column 1"
''#you can use the view as datasource or other purposes

Related

Randomly select an item from a list based on a class, repeat number of times based on different numbers

I am not familiar with using macro's, but I think that what I would like excel to perform is best handled with a macro. So I can use all the input you may have!
I have these headers;
ID Tag Pen Sex Weight Class Inside range
With 450 rows of data. Based on the distribution of the weight data, I have in two other columns (class and number) the number of rows I want to select within each class. The selected rows must have the value "Yes" in the column "Inside range".
I want to randomly select the rows, based on the number needed for each class, and copy these rows to a new sheet. It sums up to 30 rows in the new sheet.
I hope you have a suggestion how to complete this action!
can you try the following, you will need to add a reference to Microsoft Scripting Runtime library:
Const rowCount = 450
Public Sub copyRows()
Dim i As Integer
Dim j As Integer
Dim classes As Scripting.Dictionary
Dim source As Worksheet
Dim colNumber As Integer
Dim colClassName as Integer
Dim colInsideRange As Integer
Dim allSelected As Boolean
Dim randomRow as Integer
Dim sumRemaining as Integer
allSelected = False
Set source = Worksheets("YourWorksheetName")
colClassName = 6 'this is the column number where class names are entered. I am assuming 6
colNumber = 7 'this is the column number where number of rows to be selected are entered. I am assuming 7
colInsideRange = 8 'this is the column number where "Inside Range" values are entered. I am assuming 9
For i = 2 to rowCount + 1 'assuming you have a header row
classes(CStr(source.Cells(i, colClassName))) = CInt(source.cells(i, colNumber)
Next i
Do until allSelected
Randomize
randomRow = Int ((Rnd * 450) + 2) 'assuming you have a header row, + 1 if you don't
If classes(CStr(source.Cells(randomRow, colClassName))) = 0 Then
With classes
sumRemaining = 0
For j = 1 to .Count - 1
sumRemaining = sumRemaining + .Items(j)
If sumRemaining > 0 Then Exit For
Next j
allSelected = (sumRemaining = 0)
End With
Else
source.Cells(randomRow, colInsideRange) = "Yes"
classes(CStr(source.Cells(randomRow, colClassName))) = classes(CStr(source.Cells(randomRow, colClassName))) - 1
End If
Loop
'Enter your code to copy rows with "Inside Range" = "Yes"
End Sub
Sorry if there are some errors or typos, I wrote from my mobile phone.

Convert excel named Range to a collection of rows

I currently have a method which takes in a dynamic named range in excel and converts it to a 2D array.
I need to do some iterations to the data and carry out a Delete function if a certain column contains a value. I have looked at the options out there for deleting rows in 2d array using transpose and temp array and since my data is fairly large I am looking at other data structures that would make it easier to delete entire rows.
I want to convert a dynamic named range into a collection in vba. This collection will have a key the row number and as item should have all the data for that row. Basically I would need the ability to iterate through each value in that range like I can do with a 2D array but also the ability to delete a row efficiently and with less hassle than using a 2D array.
Anybody have an idea on how I can achieve this?
Dim srcArray () As Variant
Dim srcRange As Range
srcRange = ThisWorkbook.Worksheets("Main").Range("myNamedRange")
srcArray = srcRange.Value
Dim rowNr As Long
dim colNr As Long
for rowNr = 1 to UBound(srcArray,1)
if srcArray(rowNr, 9) = "testString" Then Call DeleteRowSub(srcArray, rowNr)
Next rowNr
DeleteRowSub will be a sub which will delete a given row based on the index of that row. I want to get away from that and just be able to say something like srcCollection.Remove(index) with index being the row nr.
Any help, greatly appreciated.
There's no secret to this. It's just housekeeping.
Function ReadRangeRowsToCollection(r As Range) As Collection
Dim iRow As Long
Dim iCol As Long
Dim rangeArr As Variant
Dim rowArr As Variant
Dim c As Collection
'Read range content to Variant array
rangeArr = r.Value
'Now transfer shit to collection
Set c = New Collection
For iRow = 1 To r.Rows.Count
ReDim rowArr(1 To r.Columns.Count)
For iCol = 1 To r.Columns.Count
rowArr(iCol) = rangeArr(iRow, iCol)
Next iCol
c.Add rowArr, CStr(iRow)
Next iRow
Set ReadRangeRowsToCollection = c
End Function
Example usage:
Dim c As Collection
Set c = ReadRangeRowsToCollection(Range("myNamedRange"))
c.Remove 1 ' remove first row from collection
Note: I haven't looked at edge cases; for example this will fail if the range is one cell only. Up to you to fix it.

Adding two column values to listbox in vb.net

I have a table named users which has the following columns in it
User_id,user_name,user_pwd,First_Name,Middle_Name,Last_Name and user_type.
I have dataset named dst and created a table called user in the dataset. Now I want to populate listbox with user_Name, First_Name, Last_name of each and every row in the table user.
I am able to add one column value at a time but not getting how to add multiple column values of each row to listbox
Dim dt As DataTable = Dst.Tables("user")
For Each row As DataRow In dt.Rows
lstUsers.Items.Add(row("User_Name"))
Next
Above code works perfectly but I also want to add First_name as well as last_name to the list box at the same time.
Use same approach as you have, but put all values you want in one string.
Dim dt As DataTable = Dst.Tables("user")
For Each row As DataRow In dt.Rows
Dim sItemTemp as String
sItemTemp = String.Format("{0},{1},{2}", row("User_Name"), row("First_Name"), row("Last_Name"))
lstUsers.Items.Add(sItemTemp)
Next
String.Format() function will call .ToString() on all parameters.
In this case if row(ColumnName) is NULL value then .ToString() return just empty string
You have 2 choices:
Using the ListBox:
To use the ListBox, set the font to one that is fixed width like courier new (so that the columns line up), and add the items like this:
For Each row As DataRow In dt.Rows
lstUsers.Items.Add(RPAD(row("User_Name"),16) & RPAD(row("First_Name"),16) & RPAD(row("Last_Name"),16))
Next
The RPAD function is defined like this:
Function RPAD(a As Object, LENGTH As Object) As String
Dim X As Object
X = Len(a)
If (X >= LENGTH) Then
RPAD = a : Exit Function
End If
RPAD = a & Space(LENGTH - X)
End Function
Adjust the LENGTH argument as desired in your case. Add one more for at least one space. This solution is less than ideal because you have to hard-code the column widths.
Use a DataGridView control instead of a ListBox. This is really the best option, and if you need, you can even have it behave like a ListBox by setting the option to select the full row and setting CellBorderStyle to SingleHorizontal. Define the columns in the designer, but no need to set the widths - the columns can auto-size, and I set that option in the code below. if you still prefer to set the widths, comment out the AutoSizeColumnsMode line.
The code to set up the grid and add the rows goes like this:
g.Rows.Clear() ' some of the below options are also cleared, so we set them again
g.AutoSizeColumnsMode = DataGridViewAutoSizeColumnMode.AllCells
g.CellBorderStyle = DataGridViewCellBorderStyle.SingleHorizontal
g.SelectionMode = DataGridViewSelectionMode.FullRowSelect
g.AllowUserToAddRows = False
g.AllowUserToDeleteRows = False
g.AllowUserToOrderColumns = True
For Each row As DataRow In dt.Rows
g.Rows.Add(row("User_Name"), row("First_Name"), row("Last_Name"))
Next
You might solved your problem by now but other users like me might have issue with it.
Above answers given worked for me even but I found a same answer in a simple way according to what I want..
cmd = New SqlCommand("select User_Name, First_Name, Last_Name from User")
Dim dr As SqlDataReader = cmd.ExecuteReader(YourConnectionString)
If dr.HasRows Then
Do While dr.Read
lst.Items.Add(dr.Item(0).ToString & " " & dr.Item(1).ToString & " " & dr.Item(2).ToString)
Loop
End If
This worked for me, maybe wrong way but I found it simple :)
May I suggest you use a ListView control instead of Listbox?
If you make the switch, here's a sample subroutine you could use to fill it up with the data you said you want. Adapt it the way you like; there's much room for improvement but you get the general idea:
Public Sub FillUserListView(lstUsers As ListView, Dst As DataSet)
Dim columnsWanted As List(Of String) = New List(Of String)({"User_Name", "First_Name", "Last_Name"})
Dim dt As DataTable = Dst.Tables("user")
Dim columns As Integer = 0
Dim totalColumns = 0
Dim rows As Integer = dt.Rows.Count
'Set the column titles
For Each column As DataColumn In dt.Columns
If columnsWanted.Contains(column.ColumnName) Then
lstUsers.Columns.Add(column.ColumnName)
columns = columns + 1
End If
totalColumns = totalColumns + 1
Next
Dim rowObjects(columns - 1) As ListViewItem
Dim actualColumn As Integer = 0
'Load up the rows of actual data into the ListView
For row = 0 To rows - 1
For column = 0 To totalColumns - 1
If columnsWanted.Contains(dt.Columns(column).ColumnName) Then
If actualColumn = 0 Then
rowObjects(row) = New ListViewItem()
rowObjects(row).SubItems(actualColumn).Text = dt.Rows(row).Item(actualColumn)
Else
rowObjects(row).SubItems.Add(dt.Rows(row).Item(actualColumn))
End If
lstUsers.Columns.Item(actualColumn).Width = -2 'Set auto-width
actualColumn = actualColumn + 1
End If
Next
lstUsers.Items.Add(rowObjects(row))
Next
lstUsers.View = View.Details 'Causes each item to appear on a separate line arranged in columns
End Sub

Get row index if some column value is equal to something

In this datatable there are no duplicates, I need the row index where column x value equals 2. I would do it like this:
Dim rowIndex As Integer = 0
For i = 0 To mtable.Rows.Count - 1
If mtable.Rows(i)("x") = 2 Then
rowIndex = i
Exit For
End If
Next
I will be calling this process multiple times per second. Is there a faster way to do this in .NET?
DataTable select could work, i think it should be faster than iterating over the collection of rows.
var index = mtable.Rows.IndexOf(mtable.Select("x = 2").FirstOrDefault());
Multiple times per second is a bit vague - tens or thousands?
You could create a hash table mapping the value of "x" to the row number:
Dim nLookups = mtable.Rows.Count - 1
Dim lookupHash As New Hashtable(nLookups)
For i = 0 To nLookups
lookupHash.Add(CInt(mtable.Rows(i)("x")), i)
Next
then
Dim rowSought As Integer = -1
If lookupHash.ContainsKey(2) Then
rowSought = lookupHash(2)
End If
Or if the range of possible values of "x" is suitable, you could use an array to map the value to the row number.

DataTable: How to get item value with row name and column name? (VB)

I have a simple DataTable where one of the columns contains unique values. For example:
ColumnName1 ColumnName2
value1 35
value2 44
value3 10
Because I know that value 1, 2 and 3 will always be different one from another, I would like to get a value of this table only using ColumnName2 and one of the values of ColumnName1.
That would for example be:
searchedValue = DataTable.Rows("value3").Item("ColumnName2)
'result would be 10
I tried the following examples unsuccessfully:
with the DataTable.Select method: returns an array of rows, but I only need one
with the DataTable.Rows.IndexOf method: if I understood well, I need to provide the whole row contents for it to be found with this method.
Dim rows() AS DataRow = DataTable.Select("ColumnName1 = 'value3'")
If rows.Count > 0 Then
searchedValue = rows(0).Item("ColumnName2")
End If
With FirstOrDefault:
Dim row AS DataRow = DataTable.Select("ColumnName1 = 'value3'").FirstOrDefault()
If Not row Is Nothing Then
searchedValue = row.Item("ColumnName2")
End If
In C#:
var row = DataTable.Select("ColumnName1 = 'value3'").FirstOrDefault();
if (row != null)
searchedValue = row["ColumnName2"];
'Create a class to hold the pair...
Public Class ColumnValue
Public ColumnName As String
Public ColumnValue As New Object
End Class
'Build the pair...
For Each row In [YourDataTable].Rows
For Each item As DataColumn In row.Table.Columns
Dim rowValue As New ColumnValue
rowValue.ColumnName = item.Caption
rowValue.ColumnValue = row.item(item.Ordinal)
RowValues.Add(rowValue)
rowValue = Nothing
Next
' Now you can grab the value by the column name...
Dim results = (From p In RowValues Where p.ColumnName = "MyColumn" Select p.ColumnValue).FirstOrDefault
Next
For i = 0 To dt.Rows.Count - 1
ListV.Items.Add(dt.Rows(i).Item("STU_NUMBER").ToString)
ListV.Items(i).SubItems.Add(dt.Rows(i).Item("FNAME").ToString & " " & dt.Rows(i).Item("MI").ToString & ". " & dt.Rows(i).Item("LNAME").ToString)
ListV.Items(i).SubItems.Add(dt.Rows(i).Item("SEX").ToString)
Next
Try:
DataTable.Rows[RowNo].ItemArray[columnIndex].ToString()
(This is C# code. Change this to VB equivalent)