I have a simple problem but I just don't understand any of the examples I find here or in MSDN. (I'm still new to C# and all the datasets functions).
I have a datatable "tblRoom" which its columms are "building","apartment" and "room" and they're all ints and the table's primary keys (it's a weak entity of apartment (which is weak entity of building) without other properties/columns).
I also have DataRow[] roomCollection that selects a specific apartment in a building with this code:
roomCollection = dbDataSet.tblRoom.Select("building ='"+ b_int +
"' and apartment='"+ a_int + "'");
All this runs fine (I guess...).
Now I want to get max value of room from this apartment (the max room number in this apartment).
I've tried to no avail these codes:
DataRow dr = roomCollection.Max();
int maxi = roomCollection.Max();
I just don't get from the tooltip what am I suppose to write in the function. It throws exceptions on either no IEnumerable or Icomparable.
What do I need to write to get the max value (int) in the room column? Anyone knows a "[something] for dummies" that explains it to an idiot because I don't understand the meaning of the errors/tooltip of what am I suppose to write in the Max().
EDIT:
The tooltip suggest to enter these (shown relevants):
(this IEnumerable <DataRow> source):DataRow
(this IEnumerable <DataRow> source, Func<DataRow,int?> selector):int?
(this IEnumerable <DataRow> source, Func<DataRow,int> selector):int
I really don't get it :(
Thanks ahead,
Shay.
try
int roomno = roomCollection.Max (r => (int) r["room"]);
In this case you would want to pass the selector to Max() to properly identify the column you want to max..
int max = roomCollection.Max(dr => (int)dr["room"]);
Since roomCollection is an array of DataRows, DataRow dr = roomCollection.Max(); means select the maximum DataRow from roomCollection. But what does it mean for a DataRow to be greater than another DataRow? In general there isn't any useful comparison which is why .Max() fails on you.
Technically DataRow doesn't implement IComparable because there is on generic useful why to compare two DataRow's and tell which one is 'larger' or 'smaller'. However for integers there is since 2 is larger than 1.
What you need to do is pass a selector function to Max() to tell it what maximum value you are looking for. So as the other answers indication you need to use:
int max = roomCollection.Max(dr => (int)dr["room"]);
This says for each DataRow select the integer value for room and get the maximum room value.
The .Max() function is a part of the Linq (Language Integration Query) added in .NET 3.5. So you need to read up on that to better understand what's going on. I've found 101 Linq Samples to be useful since it shows examples for almost every thing you would want to do.
Related
The following nested line gives an error but I don't really understand what is wrong. I'm coding in C#. If I look at other examples this should do the trick but unfortunately it doesn't.
var PupilsAmount= entities.Reservation.AsEnumerable().Where(x => x.AmountOfAttendants= 2.Sum(x => x.PupilsAmount));
So I want to give the sum of the value PupilsAmount only for the records where the attendants amount = 2.
Thanks in advance!
The error message is because you're calling Sum on the value 2. Sum is an extension method that operates on IEnumerable<T> (e.g. some sort of collection), not on an int.
Based on your edit, what you're after is:
var PupilsAmount= entities.Reservation.AsEnumerable().Where(x=>x.AmountOfAttendants==2).Sum(x=>x.PupilsAmount);
What's happening in my modification is a two-stage process:
First, I filter the list using Where, to get only the records where AmountOfAttendants is equal to 2
Then, I sum PupilsAmount for the remaining records.
Basically, I need to get the email column from each table in the DataSet (there could be 0 tables in there, or there could be 100) and slap them together into a big List for processing later.
I was about to write the 2x nested loop to do it, but is there an easier way to type this?
My first attempt at loops didn't work so well, since DataTables don't define GetEnumerator :
foreach(DataTable item in emailTables.Tables)
{
foreach(var email in item)
{
// This doesn't work
}
}
Like L.B. said, it's opinion based, but LINQ would be my first choice. A bit less readability but less typing overall and definitely less nesting.
var listEmail = (from DataTable table in emailTables.Tables
from DataRow row in table.Rows
select row["yourColumnNameHere"].ToString()).ToList();
If any of the tables do not (or may not) have an email column, then you will have to do some more validation.
I chose to use a separate answer to extend the question posed in a comment after my initial answer was accepted.
The question was:
I'm considering making this into an extension method, would it be possible to extend this code to obtain multiple items, like Email, Name and Address? Maybe into something other than List<>?
The answer:
Create Anonymous types in a the select statement and assign different values from the columns to those types:
var selectedItems = from DataTable table in emailTables.Tables
from DataRow row in table.Rows
select
new
{
EMail = row["email"].ToString(),
Address = row["address"].ToString(),
Name = row["name"].ToString()
};
Then loop through the results in selectedItems and do whatever you would like to the fields. Not sure what type you want to store the results in, but this should give you a pretty good idea.
foreach (var item in selectedItems)
{
//Do whatever you want by accessing the fields EMail, Address, and Name using dot notation like
var myVar = item.EMail;
var myVar2 = item.Address;
//Etc... Not sure what the end result you need is going to be, but you should have a good starting point now.
}
OR you could just return the selectedItems collection. It's type is IEnumerable<T>.
I have a large result set coming from a pretty complex SQL query. Among the values are a string which represents a location (that will later help me determine the page location that the value came from), an int which is a priority number calculated for each row based on other values from the row, and another string which contains a value I must remember for display later.
The problem is that the sql query is so complex (it has UNIONS, JOINS, and complex calculations with aliases) that I can't logically fit anything else into it without messing with the way it works.
Suffice it to say, though, after the query is done and the calculations performed, I need something that perhaps aggregate functions might solve, but that IS NOT an option, as all the columns do not come from other aggregate functions.
I have been wracking my brain for days now as to how I can iterate through the results, store a pair of values in a list (or two separate lists tied together somehow) where one value is the sum of all the priority values for each location and the other value is a distinct location value (i.e., as the results are looped through, it will not create another list item with the same location value that has been used before, HOWEVER, it does still need the sum of all of the other priority values from locations that ARE identical). Also, the results need to be ordered by priority in Descending order (hence the problem with using two lists).
EXAMPLE:
EDIT: I forgot, the preserved value should be the value from the row with the highest priority from the sql query.
If I had the following results:
location priority value
--------------------------------------------------------------------------------
page1 1 some text!
page2 3 more text!
page2 4 even more text!
page3 3 text again
page3 1 text
page3 1 still more text!
page4 6 text
If I was able to do what I wanted I would be able to achieve something like this after iteration (and in this order):
location priority value
--------------------------------------------------------------------------------
page2 7 even more text!
page4 6 text
page3 5 text again
page1 1 some text!
I have done research after research after research but absolutely nothing really even gets close to solving this dilemma.
Is what I'm asking too tough for even the powerful C# language?
THINGS I HAVE CONSIDERED:
Looping through the sql results and checking each location for repeats, adding together all priority values as I go, and storing these two plus value in two or three separate lists.
Why I still need help
I can't use a foreach because the logic didn't pan out, and I can't use a for loop because I can't access an IEnumerable (or whatever type it is that stores what's returned from Database.Open.Query() by index. (this makes sense, of course). Also, I need to sort on priority, but can't get one list out of sync with the others.
Using LINQ to select and store what I need
Why I still need help
I don't know LINQ (at all!) mainly because I don't understand lambda expressions (no matter HOW MUCH I read up about it).
Using an instantiated class to store the name/value pairs
Why I still need help
Not only do I expect sorting on this sort of thing to be impossible, and while I do now how to use .cs files in my C#.net webpages with WebMatrix environment, I have mainly only ever used static classes and would also need a little refresher course on constructors and how to set this up appropriately.
Somehow fitting this functionality into the already sizeable and complex SQL query
Why I still need help
While this is probably where I would ideally like this functionality to be, I stress again that this IS NOT AN OPTION. I have tried using aggregate functions, but only get an error saying how not all the other columns come from aggregate functions.
Making another query based on values from the first query's result set
Why I still need help
I can't select distinct results based on only one column (i.e., location) alone.
Assuming I could get the loop logic correct, storing the values in a 3 dimensional array
Why I still need help
I can't declare the array, because I do not know all of its dimensions before I need to use it.
Your post has amazed me in a number of ways like saying to 'mostly using static classes' and 'expecting instantiate a class/object to be impossible'.. really strange things you say. I can only respond in a quote from Charles Babbage:
I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question.
Anyways.. As you say you find lambdas hard, let's trace the problem in the classic 'manual' way.
Let's assume you have a list of ROWS that contains LOCATIONS and PRIORITIES.
List<DataRow> rows = .... ; // datatable, sqldatareader, whatever
You say you need:
list of unique locations
a "list" of locations paired up with summed up priorites
Let's start with the first objective.
To gather a list of unique 'values', a HashSet is just perfect:
HashSet<string> locations = new HashSet<string>();
foreach(var row in rows)
locations.Add( (string)rows["LOCATION"] );
well, and that's all. After that, the locations hashset will only remember all the unique locations. The "Add" does not result in duplicate elements. The HashSet checks and "uniquifies" all values that are put inside it. Small tricky thing is the hashset does not have the [index] operator. You'll have to enumerate the hashset to get the values:
foreach(string loc in locations)
{
Console.WriteLine(loc);
}
or convert/rewrite it to a list:
List<string> locList = new List<string>(locations);
Console.WriteLine(locList[2]); // of course, assuming there were at least three..
Let's get to the second objective.
To gather a list of values related to some thing behaving like a "logical key", a Dictionary<Key,Val> may be useful. It allows you to store/associate a "value" with some "key", ie:
Dictionary<string, double> dict = new Dictionary<string, double>();
dict["mamma"] = 123.45;
double d = dict["mamma"]; // d == 123.45
dict["mamma"] += 101; // possible!
double e = dict["mamma"]; // d == 224.45
However, it has a behavior of happily throwing exceptions when you try to read from an unknown key:
Dictionary<string, double> dict = new Dictionary<string, double>();
dict["mamma"] = 123.45;
double d = dict["daddy"]; // throws KeyNotBlarghException
dict["daddy"] += 101; // would throw too! tries to read the old/current value!
So, one have to be very careful with it with "keys" that it does not yet know. Fortunatelly, you can always ask the dictionary if it already knows a key:
Dictionary<string, double> dict = new Dictionary<string, double>();
dict["mamma"] = 123.45;
bool knowIt = dict.ContainsKey("daddy"); // == false
So you can easily check-and-initialize-when-unknown:
Dictionary<string, double> dict = new Dictionary<string, double>();
bool knowIt = dict.ContainsKey("daddy"); // == false
if( !knowIt )
dict["daddy"] = 5;
dict["daddy"] += 101; // now 106
So.. let's try summing up the priorities location-wise:
Dictionary<string, double> prioSums = new Dictionary<string, double>();
foreach(var row in rows)
{
string location = (string)rows["LOCATION"];
double priority = (double)rows["PRIORITY"];
if( ! prioSums.ContainsKey(location) )
// make sure that dictionary knows the location
prioSums[location] = 0.0;
prioSums[location] += priority;
}
And, really, that's all. Now the prioSums will know all locations and all sums of priorities:
var sss = prioSums["NewYork"]; // 9123, assuming NewYork was some location
However, that'd be quite useless to have to hardcode all locations. Hence, you also can ask the dictionary about what keys does it curently know
foreach(string key in prioSums.Keys)
Console.WriteLine(key);
and you can immediatelly use it:
foreach(string key in prioSums.Keys)
{
Console.WriteLine(key);
Console.WriteLine(prioSums[key]);
}
that should print all locations with all their sums.
You might already noticed an interesting thing: the dictionary can tell you what keys has it remembered. Hence, you do not need the HashSet from the first objective. Simply by summing up the priorities inside the Dictionary, you get the uniquized list of location by free: just ask the dict for its keys.
EDIT:
I noticed you've had a few more requests (like sort-descending or find-highest-prio-value), but I think I'll leave them for now. If you understand how I used a dictionary to collect the priorities, then you will easily build a similar Dictionary<string,string> to collect the highest-ranking value for a location. And the 'descending order' is done very easily if only you take the values out of dictionary and sort them as a i.e. List.. So I'll skip that for now.. This text got far tl;dr already I think :)
LINQ is really the tool to use for this kind of problems.
Suppose you have a variable pages which is an IEnumerable<Page>, where Page is a class with properties location, priority and value you could do
var query = from page in pages
group page by page.location into grp
select new { location = grp.Key,
priority = grp.Sum(page => page.priority),
value = grp.OrderByDescending(page => page.priority)
.First().value
}
You say you don't understand LINQ, so let me try to begin explain this statement.
The rows are group by location, which results in 4 groups of pages of which page.location is the key:
location priority value
--------------------------------------
page1 1 some text!
page2 3 more text!
4 even more text!
page3 1 text
1 still more text!
3 text again
page4 6 text
The select loops through these 4 groups and for each group it creates an anonymous type with 3 properties:
location: the key of the group
priority: the sum of priorities in one group
value: the first value in one group when its pages are sorted by priority in descending order.
The lamba expressions are a way to express which property should be used for a LINQ function like Sum. In short they say "transform page to page.priority": page => page.priority.
You want these new rows in descending order of priority, so finally you can do
result = query.OrderByDescending(x => x.priority).ToList();
The x is just an arbitrary placeholder representing one item in the collection in hand, query (likewise in the query above page could have been any word or character).
This is a real performance problem.
public int FindPreviousFC(int framecode)
{
if (SetTable == null)
throw new NullReferenceException("Not loaded Log_Roadb.");
int previousFrameCode = 0;
for (int i = 0; i < SetTable.Rows.Count; i++)
{
if (framecode == Convert.ToInt32(SetTable.Rows[i][0]))
{
previousFrameCode = Convert.ToInt32(SetTable.Rows[i - 1][0]);
break;
}
}
return previousFrameCode;
}
If the data in the SetTable is ordered on framecode than you can use a binary search through the data structure to reduce the number of lookups.
If there are not patterns in the data that you can exploit optimizing performance may become tricky. This assumes that you can't export the data from SetTable into a structure where lookups are faster.
If this Find method is being called frequently on the same set of data, then you may also want to consider creating an index structure (dictionary) to speed up subsequent lookups. This may mitigate the cost of iterating over the same data over and over.
Also, as an aside, don't throw a NullReferenceException when you check the SetTable argument, throw ArgumentNullExeception instead. Null reference exceptions are thrown by the CLR when a reference variable that is null is dereferenced ... it shouldn't be thrown by your code.
You might get some improvement by exchanging the rows with the columns in the table. Getting elements sequentially from a line in the table is faster than getting every nth element. (It has something to do with cache misses)
Most of your time is going to be spent converting text to integers. Since you say this is a time problem it sounds like you're calling this a lot--is there anything you can do to store the data as integers instead of strings?
Use a Dictionary.
Key -- SetTable.Rows[i][0]
Value -- SetTable.Rows[i-1][0].
Then when you get a framecode, just look it up in the dictionary. If it's there, return the value.
You can gain a little more efficiency by using Convert.Int32 on both the key and value before storing the in the Dictionary, then no further conversions are necessary.
Assuming
(1) SetTable is a DataTable
(2) framecodeColumn is perhaps your column name
(3) framecodeColumn occurs at index 0 (first column)
try the following:
SetTable.Select("framecodeColumn < framecodeValuePassedToYourMethod","framecodeColumn DESC")[0].[0]
Basically, find a DataRowCollection using "Select()" method by passing a filter, sort the result in descending order, and the first row will be the row that you are looking for.
Of course, please protect this line with all needed checks (like for example see if there is indeed results that satisfy the filter condition using the "GetLength()" method and so on).
I'm working with a set of legacy DAO code that returns an IList, where each Hashtable represents the row of a dynamically executed SQL query. For example, the List might contain the following records/hashtables:
Hashtable1:
Key:Column15, Value:"Jack"
Key:Column16, Value:"Stevens"
Key:Column18, Value:"7/23/1973"
Key:Column25, Value:"Active"
Hashtable2:
Key:Column15, Value:"Melanie"
Key:Column16, Value:"Teal"
Key:Column18, Value:"null"
Key:Column25, Value:"Inactive"
Hashtable3:
Key:Column15, Value:"Henry"
Key:Column16, Value:"Black"
Key:Column18, Value:"3/16/1913"
Key:Column25, Value:"Active"
Use of a static type instead of a Hashtable is out of the question because the result of the query is unknown at run time; both the number of columns and the nature of those columns is completely dynamic.
I'd like to be able to perform Linq based operations on this data set (grouping, ordering etc), but I absolutely can't get my head around what the syntax might look like. As a simple example, let's say I want to sort the list by Column15 descending. The best syntax I've come up with is:
var rawGridData = (List<Hashtable>) _listDao.GetListGridContents(listID, null, null);
var sortedGridData = rawGridData.OrderBy(s => s.Keys.Cast<string>().Where(k => k == "Column15"));
However, this yields an exception when sortedGridData is enumerated: "At least one object must implement IComparable."
I've been struggling with this problem for days and am near my wit's end...please help!
This should get you started:
var sortedGridData = rawGridData.OrderBy(r => r["Column15"])
This maps each "record" to the value in "Column15" and then orders the resulting projection. This is easily generalizable.