I currently have data in db table as follows:
Data in table
Desc Value
BNo 12
CNo Null
ANo 15
DNo Null
ENo 15
If ANo is blank i need to display BNo. If BNo is blank then display CNo on form. Once i found a non null value based on a defined priority order of DESC(In this case A-E), i need to exit my method. How can this be implemented,i can hard code desc names based on priority but it am looking to implement in a more optimized way. Any advices please.
SQL Server
select top 1 *
from MyTable
where Value is not null
order by [Desc]
MySQL:
select *
from MyTable
where `Value` is not null
order by `Desc`
limit 1
It sounds like you have the means to order the list, as you state "in a priority order". Not sure what this priority is but for the example let's assume it is in alphabetical order by "Desc". And let's also assume (for simplicity) that these values are in a dictionary. Given this you can write a Linq statement as such...
dict.OrderBy(o => o.Key).FirstOrDefault(d => d != null);
eg...
Dictionary<string,string> dict = new Dictionary<string,string>();
dict.Add("BNo", "12");
dict.Add("CNo", null);
dict.Add("ANo", null);
dict.Add("DNo", null);
dict.Add("ENo", "16");
Debug.WriteLine(dict.OrderBy(o => o.Key).FirstOrDefault(d => d.Value != null));
This will return the first non-null value. If all values are null then null is returned.
There may be some incorrect assumptions here but the OrderBy and FirstOrDefault should be able to be used in whatever manner necessary to achieve your goal. The only things that should need to change is the "o.Key" and "d.Value" to reference your actual properties.
For example, if this information is in a class such as this...
class Info
{
public string Desc { get; set; }
public int? Number { get; set; }
}
and you have a list of these such as...
List<Info> info = new List<Info>();
then you could write...
var returnValue = info.OrderBy(o => o.Desc).FirstOrDefault(d => d.Number.HasValue);
Are ANo, BNo, CNo, etc values of the Desc field in the table, or are Desc, ANo, BNo.. etc fields in the table. I get this doubt because you just that these are actually contact details.
If it is the first, RedFilter gave you an answer.
If it is the second, a dynamic query can be an option.
Just add the fields in the order of priority to an array, and iterate to build the query.
Related
I have a List<Dictionary<string,string>> something like this:
[0] key1 val,key2 val,key3 val
[1] key1 val,key2 val,key3 val
[2] key1 val,key2 val,key3 val
And i have a list of column names in the same order as columns in the datatable.
I want to filter only those keys which are there inside the list from the dictionary and also insert it in the proper order.
I'm able to filter the required keys to be inserted but then how do i insert it in the proper order in linq.
var colList = new List<string>() { "key3", "key1"};
dict.ForEach(p => jsonDataTable.Rows.Add(p.Where(q=>colList.Contains(q.key)).Select(r => r.Value).ToArray()));
I cannot do like this because number of columns will vary and also the method must work when we pass any list of column names:
foreach(var item in dict)
jsonDatatable.Rows.Add(item[colList[0]], item[colList[1]]);
Please suggest some ways.
LINQ will never ever change the input sources. You can only extract data from it.
Divide problems in subproblems
The only way to change the input sources is by using the extracted data to update your sources. Make sure that before you update the source you have materialized your query (= ToList() etc)
You can divide your problem into subproblems:
Convert the table into a sequence of columns in the correct order
convert the sequence of columns into a sequence of column names (still in the correct order)
use the column names and the dictionary to fetch the requested data.
By separating your problem into these steps, you prepare your solution for reusability. If in future you change your table to a DataGridView, or a table in an entity framework database, or a CSV file, or maybe even JSON, you can reuse the latter steps. If in future you need to use the column names for something else, you can still use the earlier steps.
To be able to use the code in a LINQ-like way, my advice would be to create extension method. If you are unfamiliar with extension methods, read Extension Methods Demystified
You will be more familiar with the layout of your table (System.Data.DataTable? Windows.Forms.DataGridView? DataGrid in Windows.Controls?) and your columns, so you'll have to create the first ones yourself. In the example I use MyTable and MyColumn; replace them with your own Table and Column classes.
public static IEnumerable<MyColumn> ToColumns(this MyTable)
{
// TODO: return the columns of the table
}
public static IEnumerable<string> ToColumnNames(this IEnumerable<MyColumn> columns)
{
return columns.Select(column => ...);
}
If the column name is just a property of the column, I wouldn't bother creating the second procedure. However, the nice thing is that it hides where you get the name from. So to be future-changes-proof, maybe create the method anyway.
You said these columns were sorted. If you want to be able to use ThenBy(...) consider returning an IOrderedEnumerable<MyColumn>. If you won't sort the sorted result, I wouldn't bother.
Usage:
MyTable table = ...
IEnumerable<string> columnNames = table.ToColumns().ToColumnNames();
or:
IEnumerable<string> columnNames = table.ToColumns()
.Select(column => column.Name);
The third subproblem is the interesting one.
Join and GroupJoin
In LINQ whenever you have two tables and you want to use a property of the elements in one table to match them with the properties of another table, consider to use (Group-)Join.
If you only want items of the first table that match exactly one item of the other table, use Join: "Get Customer with his Address", "Get Product with its Supplier". "Book with its Author"
On the other hand, if you expect that one item of the first table matches zero or more items from the other table, use GroupJoin: "Schools, each with their Students", "Customers, each with their Orders", "Authors, each with their Books"
Some people still think in database terms. They tend to use some kind of Left Outer Join to fetch "Schools with their Students". The disadvantage of this is that if a School has 2000 Students, then the same data of the School is transferred 2000 times, once for every Student. GroupJoin will transfer the data of the School only once, and the data of every Student only once.
Back to your question
In your problem: every column name is the key of exactly one item in the Dictionary.
What do you want to do with column names without keys? If you want to discard them, use Join. If you still want to use the column names that have nothing in the Dictionary, use GroupJoin.
IEnumerable<string> columNames = ...
var result = columnNames.Join(myDictionary,
columName => columName, // from every columName take the columnName,
dictionaryItem => dictionaryItem.Key, // from every dictionary keyValuePair take the key
// parameter resultSelector: from every columnName and its matching dictionary keyValuePair
// make one new object:
(columnName, keyValuePair) => new
{
// Select the properties that you want:
Name = columnName,
// take the whole dictionary value:
Value = keyValuePair.Value,
// or select only the properties that you plan to use:
Address = new
{
Street = keyValuePair.Street,
City = keyValuePair.City,
PostCode = keyValuePair.Value.PostCode
...
},
});
If you use this more often: consider to create an extension method for this.
Note: the order of the result of a Join is not specified, so you'll have to Sort after the Order
Usage:
Table myTable = ...
var result = myTable.ToColumns()
.Select(column => column.Name)
.Join(...)
.Sort(joinResult => joinResult.Name)
.ToList();
Instead of filtering on the List<Dictionary<string, string>>, filter on the colList so that you will get in the same order and only if the colList is available in the List<Dictionary<string, string>>
This is as per my understanding, please comment if you need the result in any other way.
var dictAllValues = dict.SelectMany(x => x.Select(y => y.Value)).ToList();
// Now you can filter the colList using the above values
var filteredList = colList.Where(x => dictAllValues.Contains(x));
// or you can directly add to final list as below
jsonDataTable.Rows.AddRange(colList.Where(x => dictAllValues.Contains(x)).ToList());
I need help, how do I get MAX datatable column value where value LIKE 'N01%'
Basically, if I convert this to SQL:
SELECT MAX(user) FROM tblUser WHERE user LIKE 'N01%'
Thank you.
You can simply do this:
string[] data = {"hello", "N01jaja", "N01ll"};
var userWithN1 = data.Where(we => we.StartsWith("N01")).Max();
StartsWith checks if the element starts with a certain string.
If there's a class then need to implement IComparable.
Sample code:
public class TestClass : IComparable<string>
{
public string Value { get; private set; }
public int CompareTo(string other) { return Value.CompareTo(other); }
}
var result = foo.tblUser.Where(u => u.user.StartsWith("N01")).Max(u => u.user));
Simply use a where statement to start your filter, use a StartsWith to emulate SQL's xx% pattern. Then use Max on a particular column. Though make sure User is something that will actually have a Max value.
In LINQ, I always find it helpful to break the problem down. Here in this case, you have a list of items, you want to narrow down that list with a WHERE clause and return the MAX of the remaining items.
Start
var myItems = db.GetMyList();
WHERE with LIKE
Assuming User is a string variable
myItems = myItems.Where(x=>x.User.StartsWith("N01"));
MAX
var maxItem = myItems.Max(x=>x.User);
All Together
var maxItem = db.GetMyList().Where(x=>x.User.StartsWith("N01")).Max(x=>x.User);
edit - Per comment below, since the search string was 'N01%', is should be starts with and not contains.
I have a question which refers to whether it's possible to access another property in LINQ in order to set another property's value in LINQ:
var v = ctx.myTableItems
.Where(x => x.id== anotherid)
.Select(e => new MyViewModel
{
Title = e.Title,
CurrentPrice = e.CurrenctPrice.Value,
ItemID = e.ItemID.ToString(),
Transactions= e.Transactions,
Sales =
})
.Where(x=>x.Sales>0);
So check this out guys, when I was setting my Transactions property I set the transactions collection in my previous line, and now I'd like to set my sales propert with the Transactions property that was assigned in a previous line:
Transactions= e.Transactions,
Sales = e.Transactions.Sum(x=>x.Quantity) // Normaly I'd do it like this
But I wanted to try out something like this:
Sales = Transactions.Sum(x=>x.Quantity) // is this doable ?
So my question here is, is it possible to set another property within the select statement with a previously property's value? In my case it's Transactions collection ?
Is this doable, if so , how?
P.S. I'm trying to figure if this is doable because if I could use this previously set property's value , then I would avoid performing 2 unnecesary queries on my Transactions table in DB?
You cannot use the previous value because you dont want to call e.Transactions.Sum() for every item.
Just add a method to MyViewModel
public double GetSales()
{
return Transactions.Sum(x=>x.Quantity);
}
//at the end of your code use:
.Where(x=>x.GetSales()>0);
So basically, I am reading in two XML docs. The first has two values that need to be stored: Name and Value. The second has four values: Name, DefaultValue, Type, and Limit. When reading in the docs, I want to store each into some object. I need to be able to then combine the two objects into one that has 5 values stored in it. The XML docs are different lengths, but the second will always be AT LEAST the size of the first.
EXAMPLE:
<XML1>
<Item1>
<Name>Cust_No</Name>
<Value>10001</Value>
</Item1>
<Item4>
ITEM4 NAME AND VALUE
</Item4>
<Item7>
ITEM 7 NAME AND VALUE
</Item7>
</XML1>
<XML2>
<Item1>
<Name>Cust_No</Name>
<DefaultValue></DefaultValue>
<Type>varchar</Type>
<Limit>15</Limit>
</Item1>
6 MORE TIMES ITEMS 2-7
</XML2>
I already have code looping through the XML. I really just need thoughts on what the best way to store the data it. Ultimately, I want to be able to join the two objects on the Name Key. I tried string[] and arrayList[], but I ran into difficulty combining them. I also read up on the Dictionary, but had trouble implementing that, too (I've never used the Dictionary before).
Here is Linq to Xml query, which will join two XDocuments and select anonymous objects for joined items. Each object will have five properties:
var query =
from i1 in xdoc1.Root.Elements()
join i2 in xdoc2.Root.Elements()
on (string)i1.Element("Name") equals (string)i2.Element("Name") into g
let j = g.SingleOrDefault() // get joined element from second file, if any
select new {
Name = g.Key,
Value = (int)i1.Element("Value"),
DefaultValue = (j == null) ? null : (string)j.Element("DefaultValue"),
Type = (j == null) ? null : (string)j.Element("Type"),
Limit = (j == null) ? null : (string)j.Element("Limit")
};
XDocuments created like this:
var xdoc1 = XDocument.Load(path_to_xml1);
var xdoc2 = XDocument.Load(path_to_xml2);
Usage of query:
foreach(var item in query)
{
// use string item.Name
// integer item.Value
// string item.DefaultValue
// string item.Type
// string item.Limit
}
Due to some reason I cannot change the query so I have to do this in C#.
I have a class:
public class myClass
{
int id { get; set; }
string name { get; set; }
DateTime sDate { get; set; }
bool status { get; set; }
}
The data I am getting is fetched in this list. Now what I want is to remove those properties from a list that has null values. I may sound insane but you read it right. I thought of creating another list with only the selected properties, but any of the above properties can be null. So I have to devise a mechanism to filter my list based on this.
For more clarity consider the following example.
List<myClass> lstClass = some data source.
After getting the data the generic list(lstClass) looks like this.Consider the result set in a table:
Id Name Sdate status
1 a null null
2 b null null
3 c null false
Can i some how make my list look like this after removing the property sdate.
So the new list that I want to create should have only three properties.
Id Name status
1 a null
2 b null
3 c false
Any ideas? Can I do this using Linq?
PS: This has nothing to do with presentation. I don’t have a grid where I am not able to hide columns that Is not what I am looking for.
Assuming you have a generic list of myClass instances, you can create an anonymous type with only the needed properties:
List<myClass> list = ...;
var reducedList = list.Select(e => new {e.id, e.name, e.status}).ToList();
// note: call to ToList() is optional
foreach (var item in reducedList)
{
Console.WriteLine(item.id + " " + item.name + " " + item.status);
//note: item does not have a property "sDate"
}
I'm not sure you should solve your issue in the Data, but rather it's a presentation problem.
In which control do you want to display it ? Let's say you display it in DataGrid with AutoGenerateColumns=True, then you can 1) loop on columns/properties 2) for each column/property see if all property values for all rows are null and if so set column's visibility to Collapsed.
If you generate your columns by yourself it's even simpler : only add columns when content is not null for all rows.
If your DB content is dynamic, you might want to bind each row's visibility to a property that would state wether all rows are null or not for that property. Depending on how generic you want your code to be, the code might be very different, and in case you want to have generic solution, using Reflection to retrieve/get/set properties might be of some use.