I've been looking at Dynamic Linq today (installed into VS via NuGet)...but all the examples I have found so far assume OrderBy is to be done on a known property or column name; however I am trying to OrderBy a field which is not strongly typed; but actually a key value of a row object which is derived from a Dictionary; e.g.
class RowValues : Dictionary<string, string>
{
...
}
So the list to be ordered is specifically a list of RowValues objects, filled with Name,Value pairs. For a given list of RowValues, the OrderBy field could by any of keys of the named value pairs entries (fyi: I want the orderby field to be specified in an xml config file ultimately so the ordering can be changed without re-deployment of binaries etc).
I've got a hunch the solution lies in writing a custom ordering function passed to the OrderBy??? This function would obviously know how to get a specific value from the RowValues object given a field name from the xml config....?? The answers I have seen so far show passing a string which contains a custom order by clause into the OrderBy, which is close to where I want to be, but how in my case would the runtime know where to find the fields referred to in the OrderBy string??
Input will be very much appreciated, or have I completely misunderstand the Dynamic Linq functions?
If you're using dynamic LINQ, it would just be:
var sortColumn = GetConfigValue(...);
var sorted = RowValues.OrderBy(sortColumn);
You could of course use a concatenated string to create a multiple sort ("column1, column2 DESC"). As far as I'm aware, there's no custom sort function unless you're using regular LINQ.
Also, I would make sure you know the performance characteristics of Dynamic LINQ.
Edit:
Is this what you're looking for? This will order it based on the value of the "Key" entry in the dictionary. If you need multiple sort by-s, you can use it in a loop with .ThenBy()
void Main()
{
List<RowValues> v = new List<RowValues>();
var key = "Key"; //GetFromConfig();
var v1 = new RowValues();
v1.Add("Key", "1");
v1.Add("3", "5");
var v2 = new RowValues();
v2.Add("Key", "3");
v2.Add("2", "2");
var v3 = new RowValues();
v3.Add("Key", "2");
v3.Add("2", "2");
v.Add(v1);
v.Add(v2);
v.Add(v3);
v.OrderBy(r => r[key]).Dump();
}
class RowValues : Dictionary<string, string>
{
}
Kyle, thanks again. Apologies for late reply, I have moved on from this issue now but out of interest and courtesy I wanted to come back and agree your code is much closer to where I wanted to get to, but we have lost the dynamic linq aspect. So, where you are calling the OrderBy and ordering on the key, I would want to pass a string containing the order command e.g "r[key] desc". The reason being I would want to leave the determination as to which direction to order until runtime. I suspect ths would be accomplished using an expression tree possibly? e.g: here
Related
I have this issue that I am currently stuck with in C#.
I have about 31 columns of data within Jobject inside Jarray (JArray tableJson = new JArray();)
I would like to group them into three columns.
So far I can only group by one of the columns
eg :
var tableJsonGroup = tableJson.GroupBy(x => x["FirstColumn"]).ToList();
I want to do something like this (it does not work) :
var tableJsonGroup = tableJson.GroupBy(x => new {x["FirstColumn"], x["SecondColumn"], x["FifthColumn"]}).ToList();
How do I do this?
Thank you.
As we can see, this overload of the GroupBy extension takes a delegate which is used to enumerate the tableJson and generate a key for each item.
Items that generate the same key will be grouped together and returned as an IGrouping. There is no special treatment based on the type of the source. This doesn't do anything different, whether you have an array of ints or an array of complex objects.
So, if you want to group by a combination of columns you need to provide a function that returns a unique, repeatable, key for that combination of columns.
This can be simply achieved by compounding those columns into an anonymous type, which has a built in implementation for equality and hashing that suits our purposes, like in this answer.
var groupedTableJson = tableJson.GroupBy(x =>
new {
FirstColumn: x["FirstColumn"],
SecondColumn: x["SecondColumn"],
FifthColumn: x["FifthColumn"]
});
Your answer is almost right but, you don't provide names for properties of your anonymous type. However, since you don't explain what "does not work", it is hard to be sure.
I have written a method does the following.
What it does
Parses an SqlCommand object string for parameters in a parameterized SQL query.
Excludes any parameters declared and parameters starting with ##.
Finds properties in an object using reflection that share the parameter name
Uses the object's property value and adds a parameter to the
parameter collection
Reflection
This method is designed to be used in a web environment that is highly customizable and get parameters from a variety or deserialized JSON objects. I am very aware that reflection is slow but the idea is only to reflect a single object for a return set and not to be used in data processing loops, the reflection issue is not something I care about.
Questions
Aside from listing downsides of reflection, I would like to know what issues anyone sees with this code that I might not have considered. Please make the assumption that the object has the correct properties. I am having a hard time testing this because I wrote it to parse the SQL that I know. I cannot write tests for SQL I don't know. It seems to pass every test of SQL I have written.
Is there any issue with the code
Are there potential queries that would break this?
Can the code be written more efficiently ?
Are there any other issues that you might see with this idea ?
Code
public static void LoadParametersByObject(SqlCommand command, Object obj)
{
var DeclareREG = new Regex("(?<=Declare\\s*)#\\w{1,}");// finds all Declare #name
var ParameterREG = new Regex("(#{1,2}\\w{1,})");//finds all #name and all ##name
List<String> Exclude = (from Match x in DeclareREG.Matches(command.CommandText) select x.Value.Replace("#", "").ToUpper()).ToList();
List<String> Include = (from Match x in ParameterREG.Matches(command.CommandText)
where !x.Value.StartsWith("##") && !Exclude.Contains(x.Value.Replace("#", "").ToUpper())
select x.Value.Replace("#", "").ToUpper()).Distinct().ToList();
foreach (PropertyInfo prop in (from x in obj.GetType().GetProperties() where Include.Contains(x.Name.ToUpper()) select x).ToArray())
{
command.Parameters.AddWithValue("#" + prop.Name, prop.GetValue(obj));
}
}
Code Breakdown
Use regex to find Declare(whitespace)#parameter and put it in the
exclude list
Use regex to find any parameters starting with # or ##.
throw out the ## as they are internal SQL objects and check the #
parameters to make sure they are not in the exclude list as they
were declared in the text of the query and add the results to the
include list
Iterate the include list and search for the object property with the
same name
Add the property value to the SqlCommand's SqlParameter collection
Thanks in advance for your help
I have an index which from a dictionary get the column string to order by in Linq
dict.Add(0, "seleccionado");
dict.Add(1, "IdPedido");
dict.Add(2, "FechaPedido");
dict.Add(3, "NSerie");
dict.Add(4, "Importe");
I have, too, a list of Orders and I need to get the order (DESC/ASC) when I click in the column of the field.
Mmy idea was to do the Linq in one line because I will pass two parameters: the string field to order and the direction (ASC/DESC).
How can I do it in Linq?
WHAT I TRIED:
pedidos.OrderBy(x => x.seleccionado);
seleccionado is the object of the class, but in my case I will give an string instead of the object field name.
If I understand the question, you're asking how to sort a collection by a field, where the field is chosen by the user.
In this case, why not store the lambda in the dictionary?
dict.Add(0, x => x.seleccionado);
dict.Add(1, x => x.IdPedido);
// etc.
https://stackoverflow.com/a/2728432/3825634
This has been answered before.
There are dynamic linq libraries available. They will allow you to build up parts of your linq queries dynamically with strings.
Judging from the result of my last inquiry, I need to calculate and preset the widths of a set of columns in a table that is being made into an Excel file. Unfortunately, the string data is stored in a row-based format, but the widths must be calculated in a column-based format. The data for the spreadsheets are generated from the following two collections:
var dictFiles = l.Items.Cast<SPListItem>().GroupBy(foo => foo.GetSafeSPValue("Category")).ToDictionary(bar => bar.Key);
StringDictionary dictCols = GetColumnsForItem(l.Title);
Where l is an SPList whose title determines which columns are used. Each SPListItem corresponds to a row of data, which are sorted into separate worksheets based on Category (hence the dictionary). The second line is just a simple StringDictionary that has the column name (A, B, C, etc.) as a key and the corresponding SPListItme field display name as the corresponding value. So for each Category, I enumerate through dictFiles[somekey] to get all the rows in that sheet, and get the particular cell data using SPListItem.Fields[dictCols[colName]].
What I am asking is, is there a quick or concise method, for any one dictFiles[somekey], to retrieve a readout of the longest string in each column provided by dictCols? If it is impossible to get both quickness and conciseness, I can settle for either (since I always have the O(n*m) route of just enumerating the collection and updating an array whenever strCurrent.Length > strLongest.Length). For example, suppose I had a set of 3 items, and dictCols specified the fields Field1, Field2, and Field3. The goal table might look like the following:
Item# Field1 Field2 Field3
1 Oarfish Atmosphere Pretty
2 Raven Radiation Adorable
3 Sunflower Flowers Cute
I'd like a function which could cleanly take the collection of items 1, 2, and 3 and output in the correct order...
Sunflower, Atmosphere, Adorable
Using .NET 3.5 and C# 3.0.
Unfortunately, searching for an item with the highest value for a specified observable on a set of n items means that each item must be checked. So the complexity is O(n).
This stands if no assumtion is made on the collection's order.
Having m collections to scan, the complexity (as you already figured out) is O(m x n).
EDIT [Erik Burigo]: This part of answer has been removed because it did not respond to the question's needs.
[omissis]
After having misunderstood the question I finally catched the point.
I can't see a more compact and elegant syntax than the one I'm proposing below.
var collection =
new List<Dictionary<String, String>>
{
new Dictionary<string, string> {{"Field1", "Oarfish"}, {"Field2", "Atmosphere"}, {"Field3", "Pretty"}},
new Dictionary<string, string> {{"Field1", "Raven"}, {"Field2", "Radiation"}, {"Field3", "Adorable"}},
new Dictionary<string, string> {{"Field1", "Sunflower"}, {"Field2", "Flowers"}, {"Field3", "Cute"}}
};
var fields = new[] {"Field1", "Field2", "Field3"};
var maximums = new List<String>(fields.Length);
foreach (var field in fields)
{
maximums.Add(Field(collection, field).OrderByDescending(fieldItem => fieldItem.Length).First());
}
where
static IEnumerable<String> Field(IEnumerable<Dictionary<String, String>> collection, String field)
{
foreach (var row in collection)
{
yield return row[field];
}
}
Thus relaying on an accumulator list.
This solution needs the number of fields of the various rows not to be varying from row to row (but it seems to be the case).
However, using an accumulator and an invoked method is not truly compact. What you really need is a steamlined way to transpose your data structure in order to figure the longest string for each field. As far as I know there is no shortcut in the framework to do this, thus the resulting method (Field(...)) will be tailored on your specific data structure (a collection of string-indexed strings).
Being so, the Field(...) method could be further enhanced by making it provide the longest string, thus shortening the overall invocation statement. So, the more work we put in that specific method, the more the solution approaches what you already had in mind before posting the question.
EDIT [Erik Burigo]: Changed in order to make the collection more similar to the one posted in the question.
If I have a structure like this
Albums
- Album
- Discs
- Tracks
and I want to order a collection of albums by the title of the first track on the first disc.
Is there something similar to the following I could do (keeping in mind I need to use the OrderBy extension method that accepts a string)?
albums.OrderBy("Discs[0].Tracks[0].Title")
I need to be able to sort using a string expression thus the need to use the OrderBy method i.e. albums.OrderBy("Track[0].Title"). The reason for this is our custom framework uses a sort expression (e.g. "Title") passed back from a GridView which is looked up in a dictionary (e.g. "Track[0].Title") to get the correct order by clause. That is, the field and direction of sorting is dynamically determined at runtime.
or
albums.OrderBy("Discs.First().Tracks.First().Title")
Untested, but how about:
var query = from album in albums
let disc = album.Discs.First()
let track = disc.Tracks.First()
orderby track.Title
select album;
LINQ has two ways to query "from . in .." and Lambda expressions. They way you were almost writing it looked Lambda-ish. Here would be the Lambda expression:
albums.OrderBy(a=>a.Discs.First().Tracks.First().Title)
I used variable 'a' to indicate album but you can use any variable, this is identical to the first expression:
albums.OrderBy(album=>album.Discs.First().Tracks.First().Title)
or you can use the from obj in obj form as mention in the other answers.
How about this, in order to satisfy your need for an initial query that does not perform the sorting? This uses anonymous types to store the album information, plus the name of the first track so you can sort on it later.
var query = from album in albums
let disc = album.Discs.First()
let track = disc.Tracks.First()
select new { Album = album, FirstTrack = track.Title };
var sortedQuery = from album in query
order by album.FirstTrack
select album.Album;
Sorry people,
It looks like the OrderBy method that I am asking about and trying to use is specific to the ORM (genom-e) that we are using and is not reflected on the .net Queryable or IEnumerable classes (unlike the majority of genom-e's LINQ functionality). There is no OrderBy overload that accepts a string in .net, this is specific to genom-e.
Those of you using .net encountering a similar problem should probably give either of the above two answers a try.