I have a List of String[], which I am trying to convert to a dataset/datatable using LINQ.
I parsed the text file to list, in which the first row has 4 columns and others have data associated with columns.
Everything comes up as an array in the list.
List[10] where List [0] has string[4] items.
List<string[]> list = File.ReadLines(s)
.Select(r => r.TrimEnd('#'))
.Select(line => line.Split(';'))
.ToList();
DataTable table = new DataTable();
table.Columns.AddRange(list.First().Select(r => new DataColumn(r.Value)).ToArray());
list = list.Skip(1).ToArray().ToList();
list.ForEach(r => table.Rows.Add(r.Select(c => c.Value).Cast<object>().ToArray()));
The LINQ doesn't accept the Value property.
Can some one suggest the simple and efficient way for this implementation?
System.String doesn't have a property named Value.
If you want to create a column for each item in the first row, just give it the strings:
table.Columns.AddRange(list.First().Select(r => new DataColumn(r)).ToArray());
// You don't need ToArray() here.
list = list.Skip(1).ToList();
// Get rid of Value in this line too, and you don't need
// .Select(c => c) either -- that's a no-op so leave it out.
list.ForEach(row => table.Rows.Add(row.Cast<object>().ToArray()));
There's no Dictionary here. list.First() is an array of strings. When you call Select on an array, it just passes each item in the array to the lambda in turn.
Dictionary<TKey,TValue>.Select() passes the lambda a series of KeyValuePair<TKey, TValue>. Different class, different behavior.
Related
To reduce the amount of code, is it possible to combine this in one line - where I convert a DataTable column into a string list, but I only want the distinct items in that list (there are multiple columns, so sometimes columns will have multiple values, where one won't):
List<string> column1List = returnDataTable.AsEnumerable().Select(x => x["Column1"].ToString()).ToList();
var distinctColumn1 = (from distinct1 in column1List select distinct1).Distinct();
The above works, but is an extra line. Since the distinct is an option on the list, I did try:
List<string> column1List = (returnDataTable.AsEnumerable().Select(x => x["Column1"].ToString()).ToList()).Distinct();
However, that errors, so it appears that distinct can't be called on a list being converted from a DataTable (?).
Just curious if it's possible to convert a DataTable into a string list and only get the distinct values in one line. May not be possible.
Distinct returns IEnumerable<TSource> in your case it returns IEnumerable<String> and you are trying to get the List<String> in the output.
You need to change the code from
List<string> column1List = (returnDataTable.AsEnumerable().Select(x => x["Column1"].ToString()).ToList()).Distinct();
List<string> column1List = (returnDataTable.AsEnumerable().Select(x => x["Column1"].ToString()).Distinct().ToList();
Using System.Linq you can use something like this
my_enumerable.GroupBy(x => x.Column1).Select(x => x.First).ToList()
I think what I need is relatively simple but every example I Google just returns results using First(), which I'm already doing. Here is my expression:
var options = configData.AsEnumerable().GroupBy(row => row["myColumn"]).Select(grp => grp.First());
What I need is only ONE column from the grp portion and to be able to suffix .ToList() on there without an error. As it stands I receive 4 columns, but only need a specific one, kind of like if this (grp => grp["myColumn"]), didn't result in error the Error 153 Cannot apply indexing with [] to an expression of type 'System.Linq.IGrouping<object,System.Data.DataRow>'
Also, Key does not work in the grouping portion as these results are from a DataTable object. See here - >
If you want only the keys, you can use
var options = configData.AsEnumerable().Select(row=>row["myColumn"]).Distinct();
I think that this is what you want:
configData.AsEnumerable()
.GroupBy(r => r["myColumn"])
.Select(g => new
{
myColumnValue = g.Key,
myColumnItems = g.Select(r => r["OtherColumn"]).ToList()
});
Do you understand how/what this does though? Try it out and inspect the resulting IEnumerable. I'm not sure you have a perfect understanding on how GroupBy works but take your time with above example.
See this part:
new
{
myColumnValue = g.Key,
myColumnItems = g.Select(r => r["OtherColumn"]).ToList()
}
This creates an anonymous type which outputs the values of "OtherColumn" column into a list grouped by "myColumn" where value of "myColumn" is in the myColumnValue property.
I'm not sure this answers your question but it looks like this is what you want.
The variable g is of the type IGrouping<object, DataRow>, it's not DataRow. The IGrouping interface is designed to provide a list of DataRow's grouped by object values - it does not produce a flat list, if it did then it would just be a Sort, not GroupBy.
Just specify the field you want after your call to First() e.g.
.Select(grp => grp.FirstOrDefault()["MyFieldName"]);
This will take the first record from the grouping and select the specified field from that record.
I'm getting a collection of records in a DataTable and binding it to a grid control. Before binding it I'm sorting the data based on few conditions. For brevity I'm will explain a test scenario.
I've two fields Category and Country. I want to first sort the records based on category and then by country. But the catch here is I want to push all the empty category values to the end and then sort based on the alphabetical order.
For that I'm doing -
var rows = dt.AsEnumerable()
.OrderBy(r => string.IsNullOrEmpty(Convert.ToString(r["Category"]))) //push empty values to bottom
.ThenBy(r => Convert.ToString(r["Category"]))
.ThenBy(r => Convert.ToString(r["Country"]))
But now, the fields based on which I need to sort, is dynamic which I'm having in an array.
How can I use the lambda expressions to order the records dynamically based on the fields? (pushing the empty values to the end)
I assume the array you're talking about is an array of strings.
var columns = new string[] { "Category", "Country" };
var rows = dt.AsEnumerable().OrderBy(x => 0);
foreach(var columnName in columns)
{
rows = rows.ThenBy(r => string.IsNullOrEmpty(Convert.ToString(r[category])))
.ThenBy(r => Convert.ToString(r[category]));
}
Because LINQ uses deferred execution, your query will not be evaluated until you actually need results. That's why you can construct it in multiple steps like in the example above.
ListsExtractions.dateTimeList = ListsExtractions.dateTimeList.OrderByDescending(x => x).ToList();
ListsExtractions.TextList = ListsExtractions.TextList.OrderByDescending(x => x).ToList();
FilteredLinks = FilteredLinks.OrderByDescending(x => x).ToList();
All the 3 Lists dateTimeList , TextList , FilteredLinks are type <string>
The first List dateTimeList i sorted by higher to lower. And it's fine as i wanted.
Now i need to sort the other two list to sync to the dateTimeList.
Before sorting the dateTimeList they were sync.
But now after sorting dateTimeList i need to sort the two others to the first one.
I tried to make OrderByDescending to other two Lists too but this is not the right way.
For this type of sorting you need to union your data to one data structure (before sort). For example, you may union your 3 lists of strings in one list with Tuple<string, string, string> (but it's better to create specific type for your business logic). And then sort like this:
newData.OrderByDescending(item => item.Item1).ToList()
Edit: For creating list of tuples, you may use this (for example, you have lists with names a, b, c):
var newData = a.Select((x, index) => new Tuple<string, string, string>(x, b[index], c[index])).OrderByDescending(x => x.Item1).ToList();
You can look at example in dotnetfiddle https://dotnetfiddle.net/kOh2bf
I have 2 list objects, one is just a list of ints, the other is a list of objects but the objects has an ID property.
What i want to do is sort the list of objects by its ID in the same sort order as the list of ints.
Ive been playing around for a while now trying to get it working, so far no joy,
Here is what i have so far...
//**************************
//*** Randomize the list ***
//**************************
if (Session["SearchResultsOrder"] != null)
{
// save the session as a int list
List<int> IDList = new List<int>((List<int>)Session["SearchResultsOrder"]);
// the saved list session exists, make sure the list is orded by this
foreach(var i in IDList)
{
SearchData.ReturnedSearchedMembers.OrderBy(x => x.ID == i);
}
}
else
{
// before any sorts randomize the results - this mixes it up a bit as before it would order the results by member registration date
List<Member> RandomList = new List<Member>(SearchData.ReturnedSearchedMembers);
SearchData.ReturnedSearchedMembers = GloballyAvailableMethods.RandomizeGenericList<Member>(RandomList, RandomList.Count).ToList();
// save the order of these results so they can be restored back during postback
List<int> SearchResultsOrder = new List<int>();
SearchData.ReturnedSearchedMembers.ForEach(x => SearchResultsOrder.Add(x.ID));
Session["SearchResultsOrder"] = SearchResultsOrder;
}
The whole point of this is so when a user searches for members, initially they display in a random order, then if they click page 2, they remain in that order and the next 20 results display.
I have been reading about the ICompare i can use as a parameter in the Linq.OrderBy clause, but i can’t find any simple examples.
I’m hoping for an elegant, very simple LINQ style solution, well I can always hope.
Any help is most appreciated.
Another LINQ-approach:
var orderedByIDList = from i in ids
join o in objectsWithIDs
on i equals o.ID
select o;
One way of doing it:
List<int> order = ....;
List<Item> items = ....;
Dictionary<int,Item> d = items.ToDictionary(x => x.ID);
List<Item> ordered = order.Select(i => d[i]).ToList();
Not an answer to this exact question, but if you have two arrays, there is an overload of Array.Sort that takes the array to sort, and an array to use as the 'key'
https://msdn.microsoft.com/en-us/library/85y6y2d3.aspx
Array.Sort Method (Array, Array)
Sorts a pair of one-dimensional Array objects (one contains the keys
and the other contains the corresponding items) based on the keys in
the first Array using the IComparable implementation of each key.
Join is the best candidate if you want to match on the exact integer (if no match is found you get an empty sequence). If you want to merely get the sort order of the other list (and provided the number of elements in both lists are equal), you can use Zip.
var result = objects.Zip(ints, (o, i) => new { o, i})
.OrderBy(x => x.i)
.Select(x => x.o);
Pretty readable.
Here is an extension method which encapsulates Simon D.'s response for lists of any type.
public static IEnumerable<TResult> SortBy<TResult, TKey>(this IEnumerable<TResult> sortItems,
IEnumerable<TKey> sortKeys,
Func<TResult, TKey> matchFunc)
{
return sortKeys.Join(sortItems,
k => k,
matchFunc,
(k, i) => i);
}
Usage is something like:
var sorted = toSort.SortBy(sortKeys, i => i.Key);
One possible solution:
myList = myList.OrderBy(x => Ids.IndexOf(x.Id)).ToList();
Note: use this if you working with In-Memory lists, doesn't work for IQueryable type, as IQueryable does not contain a definition for IndexOf
docs = docs.OrderBy(d => docsIds.IndexOf(d.Id)).ToList();