linq filter columns rather than data - vb.net

Firstly, I'm not sure if what I'm asking is possible or not so apologies if I'm asking a stupid question.
So I am able to filter a DataTable using linq to get the data I need, I'm wondering if it's possible to filter the columns using a simlar statement.
For example if I have the below datatable dtMyData
ID
Name
1
2
3
4
1
Conor
100
87
3
0
2
Frank
35
70
0
0
3
Jeff
35
13
0
57
I can filter it to the below using the following statement
dtMyData = dtMyData.AsEnumerable().Where(Function (f) f("Name").ToString().Equals("Frank")).CopyToDataTable
ID
Name
1
2
3
4
2
Frank
35
70
0
0
What I'm wanting to do (If it's possible) is filter the columns in a similar way so that I can select all of the columsn > 2 plus the first 2 columns. Giving me the following columns
ID
Name
3
4
1
Conor
3
0
2
Frank
0
0
3
Jeff
0
57

Take a look at this method:
Private Function CopyTable(source As DataTable, columnsToKeep As IEnumerable(Of String)) As DataTable
Dim copiedTable As DataTable = source.Clone()
Dim columnsToRemove() As DataColumn = copiedTable.Columns.Cast(Of DataColumn).Where(Function(column) Not columnsToKeep.Contains(column.ColumnName)).ToArray()
For i As Integer = 0 To columnsToRemove.Length - 1
copiedTable.Columns.Remove(columnsToRemove(i))
Next
For Each row As DataRow In source.Rows
Dim values As New List(Of Object)
For Each column As DataColumn In copiedTable.Columns
values.Add(row.Item(column.ColumnName))
Next
copiedTable.Rows.Add(values.ToArray())
Next
Return copiedTable
End Function
What this does is
Clone the DataTable
Loop over the copied DataTable and remove the columns that are not in the columnsToKeep
Loop over the original DataTable and add the rows to the copied DataTable without the cells that are not in the columnsToKeep
Fiddle: https://dotnetfiddle.net/2l6wk9
Edit
It would actually be easier to use DataTable.Copy over DataTable.Clone, my apologies:
Private Function CopyTable(source As DataTable, columnsToKeep As IEnumerable(Of String)) As DataTable
Dim copiedTable As DataTable = source.Copy()
Dim columnsToRemove() As DataColumn = copiedTable.Columns.Cast(Of DataColumn).Where(Function(column) Not columnsToKeep.Contains(column.ColumnName)).ToArray()
For i As Integer = 0 To columnsToRemove.Length - 1
copiedTable.Columns.Remove(columnsToRemove(i))
Next
Return copiedTable
End Function
What this updated code does is:
Copy the DataTable with its data
Loop over the copied DataTable and remove the columns that are not in the columnsToKeep
Fiddle: https://dotnetfiddle.net/NEIm2t

Related

datatable in vb.net keep rightmost column with data in and remove empty columns

I have a DataTable with the below data which I'm wanting to format and just have the most recent weeks data.
ID
Name
Week 1
Week 2
Week 3
Week 4
1
Conor
100
87
3
0
2
Frank
35
70
0
0
3
Jeff
35
13
0
57
I would like to keep the first 2 columns and then keep the right most column that isn't 0 giving me the following
ID
Name
Value
1
Conor
3
2
Frank
70
3
Jeff
57
I'm quite new to LINQ so I'm a little unsure if it's possible to do this or not so any help would be appreciated.
Additional Info: I forgot to mention that I'm creating the solution in UiPath (an RPA tool) so although VB Code would be better for this instance LINQ is preferable.
Linq is cool but working code is even cooler. Linq isn't necessarily faster. It does the loops internally.
Your code in the GetDataTable function would be the extraction of the data from Excel. I just built a DataTable to match your example.
In the button click event I created a table to hold the result. The outer loop goes through each row in the source DataTable. The inner For loop starts at the right most column in the dtSource and steps back to the third column (index 2). Note the Step -1. This should work for any number of Week columns since we use dtSource.Columns.Count - 1 As soon as it finds an non zero value it adds a record to dtResult and exits the inner For going on to the next row in dtSource.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim dtSource = GetDataTable()
Dim dtResult As New DataTable
dtResult.Columns.Add("ID", GetType(Integer))
dtResult.Columns.Add("Name", GetType(String))
dtResult.Columns.Add("Value", GetType(Integer))
For Each row As DataRow In dtSource.Rows
For i = dtSource.Columns.Count - 1 To 2 Step -1
If CInt(row(i)) <> 0 Then
dtResult.Rows.Add({row("ID"), row("Name"), row(i)})
Exit For
End If
Next
Next
DataGridView1.DataSource = dtResult
End Sub
Private Function GetDataTable() As DataTable
Dim dt As New DataTable
dt.Columns.Add("ID", GetType(Integer))
dt.Columns.Add("Name", GetType(String))
dt.Columns.Add("Week1", GetType(Integer))
dt.Columns.Add("Week2", GetType(Integer))
dt.Columns.Add("Week3", GetType(Integer))
dt.Columns.Add("Week4", GetType(Integer))
dt.Rows.Add({1, "Conor", 100, 87, 3, 0})
dt.Rows.Add({2, "Frank", 35, 70, 0, 0})
dt.Rows.Add({3, "Jeff", 35, 13, 0, 57})
Return dt
End Function

Update DataView based on the column value in VB.NET

I have a dataview as below:
Id | GroupName | Count
1 DummyName 1
2 DummyName 1
3 StubName 1
4 DummyName 1
I need to update the Count column value for each row based on total number of rows with the same GroupName for that row. The desired output is below:
Id | GroupName | Count
1 DummyName 3
2 DummyName 3
3 StubName 1
4 DummyName 3
The logic that I tried to do this with is:
Step 1: Update the Count column value for each row
For Each row As DataRow In tbl.Rows
row("Count") = From r in tbl _
Group r By key = tbl.Columns("sites_siteid") into Group _
where Group.Count() > 1
Select Group.Count()
Next
But it seems like I am messing up with LINQ or ....
I need to do this in VB.Net, I know how to do this in SQL but I am struggling with getting this done in Vb.Net code.
Any help or insight is highly appreciated.
Thanks in advance :)
First, create a Dictionary mapping sites_siteid to the count of rows with the same sites_siteid:
Dim countMap = (From r In tbl.Rows Group r By Key = r.Field(Of String)("sites_siteid") Into Group, Count() Select New With { .SiteId = Key, .Count = Group.Count }) _
.ToDictionary(Function(ic) ic.SiteId, Function(ic) ic.Count)
Then, update each row with the desired count:
For Each row As DataRow In tbl.Rows
row("Count") = countMap(row.Field(Of String)("sites_siteid"))
Next

read datable and store value in array

Name | CategorieID | FullCategorie_ID
---- ------------- ----------------
A 1 12
B 1 13
C 5 14
D 3 15
E 6 16
I want to read data from datatable and store a complete row in array and then return it
I cannot get the point to save a DataTable (that has rows and columns) inside an Array (that has only rows) but this is your question, not mine!
So, the code you are looking for is something like this:
Dim dt As New System.Data.DataTable
Dim arrayString(dt.Rows.Count - 1) As String
For dr As Integer = 0 To dt.Rows.Count
For dc As Integer = 0 To dt.Columns.Count
arrayString(dr) = arrayString(dr) & "$" & dt.Rows(dr).Item(dc)
'I added a special char ($) to easily split up data later
Next
Next
If you want to split up in columns your data, just use String.Split like shown below:
Dim first_array_row () as String = arrayString.Split("$")
But I higly suggest you to review your code/idea, It's better for you to find another way, because this workaround is horrible.
Instead why not use a List Class and a Dictionary Class
something like:
Dim values As New List(Of Dictionary(Of String, String))()

SELECT Unique rows from Datagridview using LINQ

I am trying to SELECT ALL rows\columns from a datagridview where the first column is unique using LINQ.
Datagridview:
1 Blue 1111
1 Blue 1111
2 Green 1234
3 Orange 3211
2 Green 1234
4 Red 2222
Trying to get this Output:
1 Blue 1111
2 Green 1234
3 Orange 3211
4 Red 2222
I was able to use the following code which pulls all unique records from the first column but I am not sure how to get the remaining columns:
Dim unique() As String = (From row As DataGridViewRow In dgvMaestro.Rows.Cast(Of DataGridViewRow)() _
Where Not row.IsNewRow _
Select CStr(row.Cells(0).Value)).Distinct.ToArray
For Each a As String In unique
Debug.Print(a)
Next
Output:
1
2
3
4
Thanks
First you need import 2 namespaces for distinct with linq to use AsEnumerable() method in DataTables and Field() method in DataRows
Imports System.Data.DataTableExtensions
Imports System.Data.DataRowExtensions
Declare new datatable
Dim NewTbl As New System.Data.DataTable
Add columns in you scenario
NewTbl.Columns.Add("ID", GetType(Integer))
NewTbl.Columns.Add("Color", GetType(String))
NewTbl.Columns.Add("Value", GetType(Integer))
Linq use Table 'TblValues' as you datasource
Dim results = (From row in TblValues.AsEnumerable() select col1 = row(Of Integer)("ID"), col2 = row(Of String)("Color"), col3 = row(Of Integer)("Value")).Distinct().ToList()
Iterate elements in results with for each, the reason is because results is an object datarow collection and isn't convert auto to table
For each r in results
Dim Row as System.Data.DataRow = NewTbl.NewRow
Row("ID") = r.col1
Row("Color") = r.col2
Row("Value") = r.col3
NewTbl.Rows.Add(Row)
next
Now you have a DataTable 'NewTbl' with distinct values inside
The best way to solve this is to write a comparer to compare the entire row.
Dim noduplicates = dgvMaestro.Rows.Cast(Of DataGridViewRow).Distinct(New RowComparer())
There are some examples of comparers on msdn

DataTable Column.Expression Throw Error

I Have Data Table which Contains Few row like below
CH1 CH2 Ch3 CH4 CH5
1 2 1 2 3
3 3 1 2 3
3 3 1 1 2
1 3 3 3 3
1 2 3 3 0
3 3 1 2 0
3 3 1 1 2
then I Try to add new column like
Dim col As New DataColumn("VCH1", GetType(Decimal),"(CH1+CH2+ch3)/CH5")
DtReadings.Columns.Add(col)
at that time give me error : Attempted to divide by zero. Because of CH5 have zero values,
but I need to add Dynamic Column with different Expression at run time ,how to avoid such type of error any Idea please Help.
Expression value not fixed,User Create expression for dynamic Column.
not only handle divide by zero error ,to handle all type of computing error
You can catch the DivideByZeroException and then assign the value you want:
Try
col = New DataColumn("VCH1", GetType(Decimal), "(CH1+CH2+ch3)/CH5")
Catch ex As DivideByZeroException
col = New DataColumn("VCH1", GetType(Decimal), "0")
End Try
DtReadings.Columns.Add(col)
The Expression syntax allow the use of the IIF statement
You could build your DataColumn using this kind of syntax for the Expression
col = New DataColumn("VCH1", GetType(Decimal), "IIF(CH5 = 0, 0, (CH1+CH2+ch3)/CH5)")
Of course, being the Expression a string property you could build your expression dynamically based on the particular requirement you have at the moment. With IIF or ISNULL you could build your string on the fly before adding the column. Something like this pseudocode
Dim currentExpression as String = BuildCurrentExpression()
col = New DataColumn("VCH1", GetType(Decimal), currentExpression)
Simply Create One Extension for Solve my Problem,that's take a time but i have no problem
<Extension()>
Public Function ToCompute(value As DataTable, exp As String, inputColumn As String) As DataTable
Dim tempdt As New DataTable
tempdt = value.Clone
tempdt.Columns(inputColumn).Expression = exp
For Each row As DataRow In value.Rows.Cast(Of DataRow).ToList
Try
tempdt.Rows.Add(row.ItemArray)
value.Rows(value.Rows.IndexOf(row))(inputColumn) = tempdt.Rows(0)(inputColumn).ToString
tempdt.Rows.Clear()
Catch ex As Exception
tempdt.Rows.Clear()
value.Rows(value.Rows.IndexOf(row))(inputColumn) = 0
Continue For
End Try
Next
Return value
End Function