Struggeling with some LinqToExcel filtering here...
Ive got a List<string> columnsToFilter that contains 9 strings, and with that i want to filter out the data for certain columns in a List<Row>, where Row contains the properties
IEnumerable<string> ColumnNames
Cell this[string columnName]
So: List<Row> has say 30 rows, each having 12 ColumnNames. Now i want to filter that List<Row> using List<string> columnsToFilter so that i end up with a List<Row> of 30 rows and 9 ColumnNames.
I can select the data for one column by quering the columnname:
var result = content.Select(m => m["Column1"]).ToList();
Now i want to filter the data based on a List of strings List<string> columnsToFilter. Whats the best way to achieve that?
Is this what you are looking for?
var colnames = new List<string>();
var rows = new Dictionary<string, object>();
var result = rows.Where(kv => colnames.Contains(kv.Key)).Select(kv => kv.Value);
Define an object called MyObject which has the property names corresponding to the 9 columns that you want to select.
var excel = new ExcelQueryFactory("excelFileName");
var myObjects = from c in excel.Worksheet<MyObject>()
select c;
Bingo. You can now iterate through the 30 objects with the 9 columns as properties.
Remember that LinqToExcel will happily fill objects even if they don't have all the columns represented.
You could even have a property or method as part of MyObject called "row" that would be a Dictionary() object so you could say myObject.row["ColumnName"] to reference a value if you preferred that syntax to just saying myObject.ColumnName to get the value. Personally I would rather deal with actual properties than to use the dictionary convolution.
I ended up doing this in two steps:
foreach (var column in columnNumbers)
{
yield return data.Select(m => m[column].Value.ToString()).ToList();
}
Now I have the data I need, but with the rows and columns swapped, so i had to swap rows for columns and vice versa:
for (int i = 1; i < rowCount; i++)
{
var newRow = new List<string>();
foreach (var cell in list)
{
newRow.Add(cell[i]);
}
yield return newRow;
}
Related
I have some datasets that I read from csv files into DataGridViews and I need to plot some charts where I count the frequency of each different variable on each column.
I found a code that count the frequency of array elements and then print them, I tried using the same logic on my datagrid.
I started with a column ("SEX" column) that has only 2 values (2.0 or 1.0), but when I try to view the count result I always get 1 (which means it didn't count a thing). It's supposed to show the count of the last different value found in the column (in my case 2.0 which has 140ish occurence).
Edit: I tried appending the count result to the textbox and I see that I end up with 1 for each loop, while I am supposed to get only 2 values (which are the count 2.0and then the count of 1.0)
I also need to plot the output, so I am guessing I could use a dictionary where I store the variable name + the frequency.
public void countFreq() //function to count the frequency of each var in a column -not working yet-
{
var n = dataGridView1.RowCount;
var visited = new bool[n];
// Traverse through array elements and
// count frequencies
for (var i = 0; i < n; i++)
{
// Skip this element if already processed
if (visited[i] || dataGridView1.Rows[i].Cells["SEX"].Value
== null)
continue;
// Count frequency
var count = 1;
for (var j = i + 1; j < n; j++)
if (dataGridView1.Rows[i].Cells["SEX"].Value == dataGridView1.Rows[j].Cells["SEX"].Value)
{
visited[j] = true;
count++;
}
textFile.Text += count.ToString(); //for testing purposes I used a textfield to print the last count value
}
}
I know that for a column where the values are explicit I can just loop on my datagrid rows and use the count method (which I did) but for most my data I don't explicitly know the values in each row so I needed to find a way to do that.
I am not sure if this is what you are looking for. In addition, the code below loops through the rows in the grid, however, if the grid has a data source I suggest looping though that collection instead of the grid rows.
Below is a method that takes a column index and returns a Dictionary<string, int>. A simple loop through each cell in the given column and if the cell's Value is not in the dictionary, we will add it. If the cells value is already in the dictionary will simply increment its int Value. After the loop finishes, the dictionary is returned. Something like…
private Dictionary<string, int> GetCountOfValues(string columnName) {
string curKey = "";
Dictionary<string, int> valuesAndCounts = new Dictionary<string, int>();
foreach (DataGridViewRow row in dataGridView1.Rows) {
if (!row.IsNewRow) {
if (row.Cells[columnName].Value != null) {
curKey = row.Cells[columnName].Value.ToString();
if (valuesAndCounts.ContainsKey(curKey)) {
valuesAndCounts[curKey]++;
}
else {
valuesAndCounts.Add(curKey, 1);
}
}
}
}
return valuesAndCounts;
}
Usage may look something like…
Dictionary<string, int> column0Counts = GetCountOfValues("Col0");
Dictionary<string, int> column1Counts = GetCountOfValues("Date");
You should really load your CSV data into a datatable and then query that
var dt = SomeFunctionThatReadsCsvIntoDatatable(..);
yourDataGridView.DataSource = dt;
Your query is then answered, simply, by grouping the datatable using linq;
(yourDataGridView.DataSource as DataTable).Rows
.Cast<DataRow>()
.GroupBy(r => r["someColumn"])
.Select(g => new { K = g.Key, C = g.Count() });
"someColumn" would be "SEX"
K would end up as an object holding whatever Type the data is ` - it's hard to tell from the information posted whether you've just got your csv as strings or whether they're eg doubles, dates etc
If you want to do it for all columns, it would probably be easiest to do it in a loop on the datatable Columns collection. DataColumn.ColumnName provides the "someColumn"
I have two list. First list has matched column which is available in second list.
I want to show matched columns with values from second list.
List<string> _filtredData = new List<string>();
_filtredData.Add("Broker");
_filtredData.Add("Loaction");
_filtredData.Add("StandardLineItem");
_filtredData.Add("Section");
foreach (DataColumn _dtCol in FinalDiffData.Columns)
{
if (matchedItems.Contains(_dtCol.ToString()))
{
_filtredData.Add(_dtCol.ToString());
}
}
_filtredData -> contains matched columns which available in Second list.
FinalDiffData.AsEnumerable() -> this is secondlist.
List<string> _filtredData = new List<string>();
_filtredData.Add("Broker");
_filtredData.Add("Loaction");
_filtredData.Add("StandardLineItem");
_filtredData.Add("Section");
foreach (DataColumn _dtCol in FinalDiffData.Columns)
{
if (matchedItems.Contains(_dtCol.ToString()))
{
_filtredData.Add(_dtCol.ToString());
}
}
var shortedListMismatchElementLocal = _filtredData;
var result = FinalDiffData.AsEnumerable().Where(p =>
shortedListMismatchElementLocal.Any());
Please help me with proper answer.
Edit from your last comment
FinalDiffData.AsEnumerable() list has column like
Broker, Loaction, StandardLineItem, Section, 2Q2019E, 3Q2019E, 4Q2019E, 2019E, 1Q2020E
etc as earning order. _filtredData list has
Broker, Loaction, StandardLineItem, Section, 2Q2019E, 3Q2019E, 4Q2019E,
I want to get matched column with value from FinalDiffData.AsEnumerable() which available in _filtredData list
You need to compare a List<string> with a DataTable. You're only interested in one column in the datatable (I'll assume the column name is "Data").
The following query will get all rows in column "Data" matching _filteredData.
var result = FinalDiffData
.AsEnumerable()
.Where(p => _filtredData.Contains(p["Data"])); // use the actual column name here
I have a small DataTable that contains a number of rows which I am running a LINQ query against.
I am having problems getting the LINQ to display the text that is in the datatable.
When I run it I can get the column name.
I have tried a number of different ways of doing it but to no avail.
Code as follows:
DataTable DTGetNarratives = DAL.GetNarrativeList();
var SelectedNarrative =
from n in DTGetNarratives.AsEnumerable()
where n.Field<string>("narr_code").Equals(ClsPublic.NarrativeCode)
select n;
foreach (var item in SelectedNarrative)
{
//test1.Add(item.Table.Columns[col].ToString());
//col++;
txtLine1.Text = item.Table.Columns[0].DefaultValue.ToString();
}
Any help on this would be great.
So you have one TextBox but an IEnumerable<DataRow>. Do you expect a single row? If not, how do you want to diplays multiple records on a single textbox?
You could comma separate them:
var allNarrCode = SelectedNarrative.Select(r => r.Field<string>("narr_code"));
txtLine1.text = string.Join(",", allNarrCode);
or as multiline TextBox use the Lines property:
txtLine1.Lines = allNarrCode.ToArray();
Only the first:
txtLine1.Text = SelectedNarrative.FirstOrDefault();
without LINQ:
foreach (DataRow row in SelectedNarrative)
{
string code = row.Field<string>("narr_code")
// the next line is pointless since you're overwriting the text on every row
//txtLine1.Text = code;
}
You can use the Field extension method like:
foreach (var item in SelectedNarrative)
{
txtLine1.Text = item.Field<string>("narr_code"); //here
}
(You can specify the column name in the method parameters)
I am not sure if you really need that since your TextBox would be populated with the last row's value.
To show all values in a single TextBox you can do:
txtLine1.Text = string.Join(" ",SelectedNarrative.Select(r=> r.Field<string>("narr_code")));
Or you can do
StringBuilder sb = new StringBuilder();
foreach (var item in SelectedNarrative)
{
sb.Append(item.Field<string>("narr_code"));
}
txtLine1.Text = sb.ToString();
I'm trying to retrieve records from a table in MS Access 2010 using OleDbDataReader. In some cases the table I am retrieving records from has multiple records for the supplied query. For example: Two tables; Ist table has two columns - one is Name(primary key) the other contains numbers. 2nd table contains many fields; one being the Name field as the foreign key. In some cases my query matches a record that in both tables returns ONE record, but on other cases there is one record from 1st table, but many records from 2nd table. So my datareader only pulls in one of those records that populates the textboxes. So I wanted to find a way to put those multiple records into a listbox and I asked the question on this forum. The answer given was to use LINQ and "put the results in a List/IEnumerable". I wish is was only that simple. I've tried to incorporate List/IEnumerable and and I'm not only going backwards, but becoming more and more confused. My understanding is that I have to create a whole new class, constructor, set of methods, etc and create a list that will take any type(int, string, etc). IS this correct? Does my new class start with
public class myList : IEnumerable<T>
{
public IEnumerable<T> GetEnumerator()
{
foreach (object o in objects)
{
yield return 0;
}
}
}
Inside method in Form class
while (myreader.Read())
{
foreach (object o in myList) //do something
}
The T being any type whereas I would otherwise use int or string?
This is getting a bit confusing...
So, basically you have a parent child relationship in your tables.
I have a very similar tutorial which will hopefully solve your problem.
Thats how the data is read
ArrayList rowList = new ArrayList();
SqlDataReader reader = storedProcCommand.ExecuteReader();
while (reader.Read())
{
object[] values = new object[reader.FieldCount];
reader.GetValues(values);
rowList.Add(values);
}
and this is how the values are added to the ListView
orderDetailsList.Items.Clear();
foreach (object[] row in rowList)
{
string[] orderDetails = new string[row.Length];
int columnIndex = 0;
foreach (object column in row)
{
orderDetails[columnIndex++] = Convert.ToString(column);
}
ListViewItem newItem = new ListViewItem (orderDetails);
orderDetailsList.Items.Add (newItem);
}
The output is something like this...
If I understand you right, you're looking to get all records from one table (I'll call it Table2) where the name matches the name on one record in another table (Table1)? I do this a lot, using LINQ and a SQL database.
I'm assuming you already have the connection to the database. Have you created a method to retrieve the desired record from Table1? If so, you already have the name. So I would create another method like this:
public List<Table2> GetList(string name)
{
List<Table2> list = new List<Table2>();
list = db.Table2.Where(q => q.NameField = name).ToList();
return list;
}
You can then call this method when populating the controls and databind the list into whatever control you want to display the list. So something like this:
List<Table2> list = new List<Table2>();
list = GetList("This is the name from the Table1 data");
listviewcontrol.DataSource = list;
The method for filling the control with the data varies a bit depending on which control you're using. EDIT: Rwiti has some good code there for adding the data to the listview.
If you want to display just part of the data from the list of Table2 records (for example, a Location field to populate a combobox), I'd suggest something like this:
List<string> locations = new List<string>();
foreach (Table2 record in list)
{
locations.Add(record.Location);
}
comboBox1.DataSource = locations;
I Have one list and one comma separated column value.
lst.Add("Beauty");
lst.Add("Services");
lst.Add("Others");
And Column value is like
Beauty,Services
Service,Others
Beauty,Others
Other,Services
Other
Beauty, Food
Food
Now I Want all those rows which contains any list item.
Output result is
Beauty,Services
Service,Others
Beauty,Others
Other,Services
Other
Beauty, Food
First Edit
These comma separated values are one of my table's one column value.
I need to check against that column.
Assuming you can figure out how to get the RowCollection
RowCollection.Where(row => row.Split(",").ToList().Where(x => list.Contains(x)).Any()).ToList();
will evaluate to true if the row contains a value from the list.
try
List<string> lst = ....; // your list of strings
string[] somecolumnvalues = new string[] { "", "",... }; // your columvalues
var Result = from l in lst from c in somecolumnsvalues where c.Contains (l) select c;