I have the following ItemArray:
dt.Rows[0].ItemArray.. //{0,1,2,3,4,5}
the headers are : item0,item1,item2 etc..
So far, to get a value from the ItemArray I used to call it by an index.
Is there any way to get the value within the ItemArray with a Linq expression based on the column name?
Thanks
You can also use the column-name to get the field value:
int item1 = row.Field<int>("Item1");
DataRow.Item Property(String)
DataRow.Field Method: Provides strongly-typed access
You could also use LINQ-to-DataSet:
int[] allItems = (from row in dt.AsEnumerable()
select row.Field<int>("Item1")).ToArray();
or in method syntax:
int[] allItems = dt.AsEnumerable().Select(r => r.Field<int>("Item1")).ToArray();
If you use the Item indexer rather than ItemArray, you can access items by column name, regardless of whether you use LINQ or not.
dt.Rows[0]["Column Name"]
Tim Schmelter's answer is probably what you are lookin for, just to add also this way using Convert class instead of DataRow.Field:
var q = (from row in dataTable.AsEnumerable() select Convert.ToInt16(row["COLUMN1"])).ToArray();
Here's what I've come up with today solving a similar problem. In my case:
(1)I needed to xtract the values from columns named Item1, Item2, ... of bool type.
(2) I needed to xtract the ordinal number of that ItemN that had a true value.
var itemValues = dataTable.Select().Select(
r => r.ItemArray.Where((c, i) =>
dataTable.Columns[i].ColumnName.StartsWith("Item") && c is bool)
.Select((v, i) => new { Index = i + 1, Value = v.ToString().ToBoolean() }))
.ToList();
if (itemValues.Any())
{
//int[] of indices for true values
var trueIndexArray = itemValues.First().Where(v => v.Value == true)
.Select(v => v.Index).ToArray();
}
forgot an essential part: I have a .ToBoolean() helper extension method to parse object values:
public static bool ToBoolean(this string s)
{
if (bool.TryParse(s, out bool result))
{
return result;
}
return false;
}
Related
For example, I have a list of excel cells
List<Cell> cells = new List<Cell>
{
new Cell("4"),
new Cell("Hez"),
new Cell("Method"),
new Cell("4"),
new Cell("Val"),
new Cell("Method"),
}
I need to get the only unique cell (in this case Cell("Val"), Cell("Hez")) so Distinct() is not for me.
I found this solution but it doesn't return any data at all
var uniqueTest = allData.GroupBy(cell => cell)
.Where(group => group.ToString().Count() == 1)
.Select(group => group.Key);
I think the problem is Cell object doesn't contain any comparison methods (This is IronXl lib) so this is why I'm using ToString() here.
But I don't quite understand linq yet, so any explanation or advice is appreciated
Remarks:
I need to get a list of cells back, but with unique values
Step 1: Group the cells by their value.
Step 2: Keep only the groups of size 1.
Step 3: Get the only item from each group.
var uniqueCells =
allData.GroupBy(cell => cell.Value) //Step 1
.Where(g => g.Count() == 1) //Step 2
.Select(g => g.Single()) //Step 3
This should be easy.
Lets count the value of Number 4 is Key.. then your linq should look like this
var uniqueTest = allData.GroupBy(x=> x.Key).Select(x=> x.First()).Select(x=> x.Key);
If I understand you correctly, and i'm not sure if I do, you can use .First() or the more robust .Single()
var uniqueTest = allData.First(c => c.ToString() == "Val");
In this sample i'm assuming c.ToString() will give you the cell's value. Otherwise it will likely be something like c.Value or something.
There are als the OrDefault variants.
Check out this article for the differences;
https://www.c-sharpcorner.com/blogs/singleordefault-vs-firstordefault-in-linq-query1
if you class Cell is something like this.
public class Cell
{
public Cell(string mystring)
{
MyString = mystring;
}
public string MyString { get; set; }
then this will work to get a unique list:
List<Cell> UniqueCells = new List<Cell>();
foreach (var cell in cells)
{
if(!UniqueCells.Any(c=>c.MyString == cell.MyString))
{
UniqueCells.Add(cell);
}
}
In this case only the first cell containing 'Method' and '4' will be added. the '!' and '.Any' are the essential parts.
How can I get the string from a list that best match with a base string using the Levenshtein Distance.
This is my code:
{
string basestring = "Coke 600ml";
List<string> liststr = new List<string>
{
"ccoca cola",
"cola",
"coca cola 1L",
"coca cola 600",
"Coke 600ml",
"coca cola 600ml",
};
Dictionary<string, int> resultset = new Dictionary<string, int>();
foreach(string test in liststr)
{
resultset.Add(test, Ldis.Compute(basestring, test));
}
int minimun = resultset.Min(c => c.Value);
var closest = resultset.Where(c => c.Value == minimun);
Textbox1.Text = closest.ToString();
}
In this example if I run the code I get 0 changes in string number 5 from the list, so how can I display in the TextBox the string itself?
for exemple : "Coke 600ml" Right now my TextBox just returns:
System.Linq.Enumerable+WhereEnumerableIterator`1
[System.Collections.Generic.KeyValuePair`2[System.String,System.Int32]]
Thanks.
Try this
var closest = resultset.First(c => c.Value == minimun);
Your existing code is trying to display a list of items in the textbox. I looks like it should just grab a single item where Value == min
resultset.Where() returns a list, you should use
var closest = resultset.First(c => c.Value == minimun);
to select a single result.
Then the closest is a KeyValuePair<string, int>, so you should use
Textbox1.Text = closest.Key;
to get the string. (You added the string as Key and changes count as Value to resultset earilier)
There is a good solution in code project
http://www.codeproject.com/Articles/36869/Fuzzy-Search
It can be very much simplified like so:
var res = liststr.Select(x => new {Str = x, Dist = Ldis.Compute(basestring, x)})
.OrderBy(x => x.Dist)
.Select(x => x.Str)
.ToArray();
This will order the list of strings from most similar to least similar.
To only get the most similar one, simply replace ToArray() with First().
Short explanation:
For every string in the list, it creates an anonymous type which contains the original string and it's distance, computed using the Ldis class. Then, it orders the collection by the distance and maps back to the original string, so as to lose the "extra" information calculated for the ordering.
How can the query below be modified to include a column for row number (ie: one-based index of results)?
var myResult = from currRow in someTable
where currRow.someCategory == someCategoryValue
orderby currRow.createdDate descending
select currRow;
EDIT1: I'm looking for the results to be {idx, col1, col2...col-n} not {idx, row}.
EDIT2: The row number should correspond to result rows not the table rows.
EDIT3: I DataBind these results to a GridView. My goal was to add a row number column to the GridView. Perhaps a different approach would be better.
Use the method-syntax where Enumerable.Select has an overload with the index:
var myResult = someTable.Select((r, i) => new { Row = r, Index = i })
.Where(x => x.Row.someCategory == someCategoryValue)
.OrderByDescending(x => x.Row.createdDate);
Note that this approach presumes that you want the original index of the row in the table and not in the filtered result since i select the index before i filter with Where.
EDIT: I'm looking for the results to be {idx, col1, col2...col-n} not
{idx, row}. The row number should correspond to result rows not
the table rows.
Then select the anonymous type with all columns you need:
var myResult = someTable.Where(r => r.someCategory == someCategoryValue)
.OrderByDescending(r => r.createdDate)
.Select((r, i) => new { idx = i, col1 = r.col1, col2 = r.col2, ...col-n = r.ColN });
Use this Select method:
Projects each element of a sequence into a new form by incorporating the element's index.
Example:
var myResult = someTable.Where(currRow => currRow.someCategory == someCategoryValue)
.OrderByDescending(currRow => currRow.createdDate)
.Select((currRow, index) => new {Row = currRow, Index = index + 1});
In response to your edit:
If you want a DataTable as result, you can go the non-Linq way by simply using a DataView and add a additional column afterwards.
someTable.DefaultView.RowFilter = String.Format("someCategory = '{0}'", someCategoryValue);
someTable.DefaultView.Sort = "createdDate";
var resultTable = someTable.DefaultView.ToTable();
resultTable.Columns.Add("Number", typeof(int));
int i = 0;
foreach (DataRow row in resultTable.Rows)
row["Number"] = ++i;
what about?
int i;
var myResult = from currRow in someTable
where currRow.someCategory == someCategoryValue
orderby currRow.createdDate descending
select new {Record = i++, currRow};
Just for fun, here's an alternative to Select with two arguments:
var resultsWithIndexes = myResult.Zip(Enumerable.Range(1, int.MaxValue - 1),
(o, i) => new { Index = i, Result = o });
According to you edit 1. NO, YOU CAN'T Linq returns the table as it is. You can build each column, but you lose the power of mapped entities.
This has been asked multiple times before: How do you add an index field to Linq results
There is no straightforward way if want to keep a flat list of columns (i.e. OP's Edit2) and also want a generic solution that works with any IEnumerable without requiring you to list out the set of expected columns.
However, there is a roundabout way to kinda go about it which is to dump the query results into a DataTable using the ToDataTable() method from here and then add a RowNumber column to that table.
var table = query.ToList().ToDataTable();
table.Columns.Add("RowNum", typeof(int));
int i = 0;
foreach (DataRow row in table.Rows)
row["RowNum"] = ++i;
This would likely cause performance issues with large datasets but it's not insanely slow either. On my machine a dataset with ~6500 rows took 33ms to process.
If your original query returned an anonymous type, then that type definition will get lost in the conversion so you'll lose the static typing on the column names of the resulting IEnumerable when you call table.AsEnumerable(). In other words, instead of being able to write something like table.AsEnumerable().First().RowNum you instead have to write table.AsEnumerable().First()["RowNum"]
However, if you don't care about performance and really want your static typing back, then you can use JSON.NET to convert the DataTable to a json string and then back to a list based on the anonymous type from the original query result. This method requires a placeholder RowNum field to be present in the original query results.
var query = (from currRow in someTable
where currRow.someCategory == someCategoryValue
orderby currRow.createdDate descending
select new { currRow.someCategory, currRow.createdDate, RowNum = -1 }).ToList();
var table = query.ToDataTable();
//Placeholder RowNum column has to already exist in query results
//So not adding a new column, but merely populating it
int i = 0;
foreach (DataRow row in table.Rows)
row["RowNum"] = ++i;
string json = JsonConvert.SerializeObject(table);
var staticallyTypedList = JsonConvert.DeserializeAnonymousType(json, query);
Console.WriteLine(staticallyTypedList.First().RowNum);
This added about 120ms to the processing time for my 6500 item dataset.
It's crazy, but it works.
I know I'm late to the party, but I wanted to show what worked for me.
I have a list of objects, and the object has an integer property on it for "row number"... or in this case, "Sequence Number". This is what I did to populate that field:
myListOfObjects = myListOfObjects.Select((o, i) => { o.SequenceNumber = i; return o; }).ToList();
I was surprised to see that this worked.
This one helped me in my case - Excel sheet extraction. anonymous type
var UploadItemList = ItemMaster.Worksheet().AsEnumerable().Select((x, index) => new
{
Code = x["Code"].Value == null ? "" : x["Code"].Value.ToString().Trim(),
Description = x["Description"].Value == null ? "" : x["Description"].Value.ToString().Trim(),
Unit = x["Unit"].Value == null ? "" : x["Unit"].Value.ToString().Trim(),
Quantity = x["Quantity"].Value == null ? "" : x["Quantity"].Value.ToString().Trim(),
Rate = x["Rate"].Value == null ? "" : x["Rate"].Value.ToString().Trim(),
Amount = x["Amount"].Value == null ? "" : x["Amount"].Value.ToString().Trim(),
RowNumber = index+1
}).ToList();
int Lc = 1;
var Lst = LstItemGrid.GroupBy(item => item.CategoryName)
.Select(group => new { CategoryName = group.Key, Items = group.ToList() ,RowIndex= Lc++ })
.ToList();
How can I check the results of LINQ query for a specific string value?
I have the following linq query:
IEnumerable<DataRow> rows = searchParamsTable.AsEnumerable()
.Where(r => r.Field<String>("TABLE") == tableNumbers[i].ToString()
&& r.Field<String>("FIELD ") == fieldName[i]);
I want to see if the result of that query contains a string(passed in form a text box) "wildcardSearchString".
var searchRows =
rows.Where(tr => tr.ItemArray
.Any(ti => ti.ToString().IndexOf("wildcardSearchString", StringComparison.CurrentCultureIgnoreCase) > 0))
This will go through each of the rows that was returned, and see if "wildcardSearchString" is in the rows item string representation (ignoring case). Here's the problem though, this won't get you wildcard search support, so you'll have to figure that one out yourself. You can try to use Regex, which would require a slight modification:
string searchPattern = "some*string".Replace("*", ".*");
var searchRows =
rows.Where(tr => tr.ItemArray
.Any(ti => Regex.IsMatch(ti.ToString(), searchPattern)))
Hope that helps. Just be warned that if they decide to try supplying a Regex pattern than this might really mess up whatever they were searching for, so you just need to be careful of input.
try with this code
DataRow[] array = rows.ToArray();
array.Contains(yourIndex, yourTextBox.Text);
Add this extension
public static bool Contains(this DataRow[] dataRows, string value, int index)
{
foreach(var row in dataRows)
{
if(row[index].ToString().Contains(value))
{
return true;
}
}
return false;
}
Boolean found = false;
foreach(Datarow d in rows)
{
foreach(object o in d.ItemArray)
{
if(o.ToString().Contains("test")
{
found=true;
break;
}
}
}
Do you mean something like this?
I don't know if you're aware of the built-in search capabilities of a DataTable? You could use its Select method:
DataRow[] rows = searchParamsTable
.Select("TABLE = 'Table1' AND FIELD like '%wildcardSearchString%'");
Linq is OK but not always required :).
I have a datatable which contains a load of dates. I wanted to group these by date and give each row a count.
I have managed to do this by dong the following:
IEnumerable query = from row in stats.AsEnumerable()
group row by row.Field<string>("date") into grp
select new { Date = grp.Key, Count = grp.Count(t => t["date"] != null) };
(where "stats" is the datatable)
I can see from debugging that this brings back the values all grouped as I need, but now I need to loop them and get each date and count.
My problem is I don't know how to retrieve the values!
I have a foreach loop
foreach (var rw in query)
{
string date = rw.Date; // <---- this is my problem?
}
I don't know what type my Ienumerable is to be able to reference the values in it!
So my question is how can I retrieve each date and count for each row by doing similar to the above?
I hope this makes sense!
This link on my blog should help you
http://www.matlus.com/linq-group-by-finding-duplicates/
Essentially your type is an anonymous type so you can't reference it as a type but you can access the properties like you're trying to do.
I think I see your issue. If you're trying to return it from a method, you should define a type and reuturn it like shown below:
public IEnumerable<MyType> GetQuery()
{
var query = from row in stats.AsEnumerable()
group row by row.Field<string>("date") into grp
select new { Date = grp.Key, Count = grp.Count(t => t["date"] != null) };
foreach (var rw in query)
{
yield return new MyType(rw.Date, rw.Count);
}
}
declare your "query" variable using "var" as shown above.
I guess you don't have access to the properties of the anonymous class because you're using IEnumerable query = .... Try var query = ... instead.
Going by your comment "I am returning the query from a function", which I take to mean that you want to do the query in a method, return the data to the caller, and then iterate the data in the caller, I suggest you return a Dictionary<DateTime, int>, like this:
static Dictionary<DateTime, int> GetSummarisedData()
{
var results = (
from row in stats.AsEnumerable()
group row by row.Field<string>("date") into grp
select new { Date = grp.Key, Count = grp.Count(t => t["date"] != null) })
.ToDictionary(val => val.Date, val => val.Count);
return results;
}
then in the caller you can just
foreach (var kvp in GetSummarisedData())
{
// Now kvp.Key is the date
// and kvp.Value is the count
}