I am trying to add elements into a list, order them and then output them, there a number of "columns" if you like, per list
List<Info> infoList = new List<Info>();
while (dr.Read())
{
meeting_id = dr.GetValue(0).ToString();
try
{
Appointment appointment = Appointment.Bind(service, new ItemId(meeting_id));
Info data = new Info();
data.Start = appointment.Start;
data.Fruit = Convert.ToInt32(dr.GetValue(1));
data.Nuts = Convert.ToInt32(dr.GetValue(2));
infoList.Add(data);
}
Then to output it I want to order it by Start and then display all associated columns
for (int i = 0; i < infoList.Count; i++)
{
meet = meet + infoList[i];
}
First question: is the way I am inputting the data right?
Second question: How to I output all the columns to display all the associated columns? Is this possible? Is there a better practice?
Thanks
EDIT:
The class if you are interested:
public class Info
{
public DateTime Start { get; set; }
public int Fruit { get; set; }
public int Nuts { get; set; }
}
You can use Enumerable.OrderBy extension for enumerating your collection in some particular order (e.g. ordered by Start property value):
foreach(var info in infoList.OrderBy(i => i.Start))
{
// use info object here
// info.Fruits
// info.Nuts
}
BTW consider to add sorting on database side - that will be more efficient
Related
I have an sql query that provides me my data where I sometimes have lines that should be clustered (the data is aligned with an order by). The data is grouped by the field CAPName. Going through those rows line by line, I need to decide whether a new list should be initiated (content of CAPName differs to previous itteration), or whether the (already) initated list (from the previous iteration) should be added, too.
My pain lays with the location of the declaration of the relatedCapabilitySystem list.
I wanted to declare it within the if statement (Because, as I stated I need to decide whether the list from the previous iteration should be added too, or whether it should start a new list), but I can't as the compiler throws an exception, as the RLCapSys.Add(rCs); is non-existing in this content (which is only theoretically true). I understand why the compiler throws this exception. But if I declare the list on a "higher" level, than I always have a new list, which I don't want in the case that the item should be added to the list defined in the iteration(s) (1 or more) before
So what I want to achieve is, generate the list RLCapSys and add to it, in case the previous iteration contains the same CAPName (for clustering), otherwise create a new list.
SqlCommand cmdDetail = new SqlCommand(SQL_SubSytemsToCapability, DBConDetail);
SqlDataReader rdrDetail = cmdDetail.ExecuteReader();
List<relatedCapility> RLCaps = new List<relatedCapility>();
string lastCapShown = null;
while (rdrDetail.Read())
{
List<relatedCapabilitySystem> RLCapSys = new List<relatedCapabilitySystem>();
if (lastCapShown != rdrDetail["CAPName"].ToString())
{
//List<relatedCapabilitySystem> RLCapSys2 = new List<relatedCapabilitySystem>();
relatedCapility rC = new relatedCapility
{
Capability = rdrDetail["CAPName"].ToString(),
systemsRelated = RLCapSys,
};
RLCaps.Add(rC);
}
relatedCapabilitySystem rCs = new relatedCapabilitySystem
{
system = rdrDetail["name"].ToString(),
start = rdrDetail["SysStart"].ToString(),
end = rdrDetail["SysEnd"].ToString(),
};
RLCapSys.Add(rCs);
// method to compare the last related Capability shown create a new related Capabilty entry or add to the existing releated Capabilty related system list
lastCapShown = rdrDetail["CAPName"].ToString();
}
DBConDetail.Close();
and for reason of completness (but I think it is not needed here):
internal class CapabilitiesC
{
public List<Capability>Capabilities{ get;set;}
}
public class Capability
{
public string name { get; internal set; }
public string tower { get; internal set; }
public string color { get; internal set; }
public List<relatedCapility> related { get; set; }
}
public class relatedCapility
{
public string Capability { get; set; }
public List<relatedCapabilitySystem> systemsRelated { get; set; }
}
public class relatedCapabilitySystem
{
public string system { get; set; }
public string start { get; set; }
public string end { get; set; }
}
The purpose of your code is to take the input data and group it by capability. However, that is not immediately obvious. You can change your code to use LINQ so it becomes easier to understand and in the process solving your problem.
First you need a type to represent a record in your database. For lack of better name I will use Record:
class Record
{
public string System { get; set; }
public string Start { get; set; }
public string End { get; set; }
public string Capabilty { get; set; }
}
You can then create an iterator block to return all the records from the database (using an OR mapper like Entity Framework avoids most of this code and you can even shift some of the work from your computer to the database server):
IEnumerable<Record> GetRecords()
{
// Code to create connection and command (preferably in a using statement)
SqlDataReader rdrDetail = cmdDetail.ExecuteReader();
while (rdrDetail.Read())
{
yield return new Record {
System = rdrDetail["name"].ToString(),
Start = rdrDetail["SysStart"].ToString(),
End = rdrDetail["SysEnd"].ToString(),
Capability = rdrDetail["CAPName"].ToString()
};
}
// Close connection (proper using statement will do this)
}
Finally, you can use LINQ to perform the grouping:
var RLCaps = GetRecords()
.GroupBy(
record => record.Capability,
(capability, records) => new relatedCapility
{
Capability = capability ,
systemsRelated = records
.Select(record => new relatedCapabilitySystem
{
system = record.System,
start = record.Start,
end = record.End
})
.ToList()
})
.ToList();
Why not just assign it as NULL. The pattern would be
List<> myList = null;
if(condition)
{
myList = new List<>();
}
else
{
myList = previousList;
}
myList.Add();
previousList = myList;
I've got it working now. Thx everyone for your help. #martin, thx for your solution, you have put quite some effort into this, but that would have required for me to completely re-write my code. I am sure your approach would work and will be my next approach should I have a similar problem again.
It was a combination of the other answers that helped me figure it out. Let me show you what I ended up with:
SqlCommand cmdDetail = new SqlCommand(SQL_SubSytemsToCapability, DBConDetail);
SqlDataReader rdrDetail = cmdDetail.ExecuteReader();
List<relatedCapility> RLCaps = new List<relatedCapility>();
List<relatedCapabilitySystem> RLCapSys = new List<relatedCapabilitySystem>();
string lastCapShown = null;
while (rdrDetail.Read())
{
if (lastCapShown != rdrDetail["CAPName"].ToString())
{
RLCapSys = relatedCapabilitySystemList();
relatedCapility rC = new relatedCapility
{
Capability = rdrDetail["CAPName"].ToString(),
systemsRelated = RLCapSys,
};
RLCaps.Add(rC);
}
relatedCapabilitySystem rCs = new relatedCapabilitySystem
{
system = rdrDetail["name"].ToString(),
start = rdrDetail["SysStart"].ToString(),
end = rdrDetail["SysEnd"].ToString(),
};
RLCapSys.Add(rCs);
// method to compare the last related Capability shown create a new related Capabilty entry or add to the existing releated Capabilty related system list
lastCapShown = rdrDetail["CAPName"].ToString();
}
DBConDetail.Close();
So that's the section already shown bevor including my changes. Plus I added this:
private List<relatedCapabilitySystem> relatedCapabilitySystemList()
{
List<relatedCapabilitySystem> RLCapSys = new List<relatedCapabilitySystem>();
return RLCapSys;
}
Now I have new list reference everytime the CapName changes that is then added to the "higher" list. Before I had the issue of the very same list repeatedly assigned rather than a fresh one started. So thx again for your effort.
I have stored the result of a stored procedure (in Entity Framework) in an IList and then bind my grid with this IList. When this result is null the grid hasn't got any columns but I need to show these columns in the grid. Is there any way to solve this problem?
This is my code:
IList list = new ArrayList();
try
{
var context = new SabzNegar01Entities1();
list = (from p in context.tbl_ReturnSalesFactor_D
let add = (p.MainNum * p.Fee)
let pureAdd = ((p.MainNum * p.Fee) - (p.MainNum * p.Discount)) + ((p.Tax + p.Charges) * p.MainNum)
let taxChange = (p.Tax + p.Charges) * p.MainNum
let discount = p.Discount * p.MainNum
where p.DocNo == inDocNo
select new { p.Row, p.StockCode, p.tbl_Stock.PDescription, p.Fee, p.MainNum, add, taxChange, discount, pureAdd }).ToList();
}
catch (Exception ex)
{
PMessageBox.Show(ex.Message, "Error in Reading ReturnSalesFactor_Details Data");
}
and binding:
radGridView_Product.DataSource = list ;
I would do this:
define a C# class that matches the data that you're getting back from the stored procedure (e.g. SalesInfo or whatever you want to call it)
then define your IList to be a List<SalesInfo> (please don't use the crappy old ArrayList anymore!)
when you call the stored procedure, but you get no values back, you just add a dummy SalesInfo entry to your list being returned, that e.g. has no data found as its description and everything else is empty/0.0
That way, your method will always return at least one element, and since that element is there, the gridview know it's columns and what to call them
Update:
I would first define a class to hold all those properties you want to display in your gridview:
// just a data container to hold the information - call it whatever you like!
// also: with the datatypes, I am just *GUESSING* because you didn't exactly tell us
// what those values are - adapt as needed !
public class SalesInfo
{
public int Row { get; set; }
public string StockCode { get; set; }
public string Description { get; set; }
public decimal Fee { get; set; }
public decimal MainNum { get; set; }
public decimal Add { get; set; }
public decimal TaxChange { get; set; }
public decimal Discount { get; set; }
public decimal PureAdd { get; set; }
}
Next, define a method that goes and gets that data from the stored procedure - if not data is returned, add a dummy entry instead:
// Define a method to return an IList of that data container class defined above
public IList<SalesInfo> GetSalesInfo()
{
// please, as of .NET 2.0 - use the List<T> and stop using ArrayList!
IList<SalesInfo> list = new List<SalesInfo>();
try
{
// put your context usage into using()..... blocks to ensure proper disposal
using (var context = new SabzNegar01Entities1())
{
// fetch the data, turn it into SalesInfo records
list = (from p in context.tbl_ReturnSalesFactor_D
where p.DocNo == inDocNo
select new SalesInfo
{
Row = p.Row,
StockCode = p.StockCode,
Description = p.tbl_Stock.PDescription,
Fee = p.Fee,
MainNum = p.MainNum,
Add = p.MainNum*p.Fee,
PureAdd = ((p.MainNum*p.Fee) - (p.MainNum*p.Discount)) + ((p.Tax + p.Charges)*p.MainNum),
Discount = p.Discount*p.MainNum,
TaxChange = (p.Tax + p.Charges)*p.MainNum
}).ToList();
}
// if you get back no data -> add a dummy entry
if (list.Count <= 0)
{
list.Add(new SalesInfo { Description = "(no data found)" });
}
}
catch (Exception ex)
{
PMessageBox.Show(ex.Message, "Error in Reading ReturnSalesFactor_Details Data");
}
// return the resulting list
return list;
}
This all originates from querying Google Analytics data. For a basic query the main factors that change are the dimensions and the metrics. The object that is returned is of a type called GaData, and the actual results that you need reside in GaData.Rows.
The format of GaData.Rows looks like this:
There will first be a row for each dimension, in this example there is a row for "New Visitor" and a 2nd row for "Returning Visitor". Within those rows will be another set of rows that contain the Dimension value, and then each metric that you specify (I've only asked for one metric).
So far the class setup I have is as follows:
public class Results
{
public List<Dimension> Dimensions { get; set; }
}
public class Dimension
{
public string Value { get; set; }
public List<Metric> Metrics { get; set; }
}
public class Metric
{
public int Value { get; set; }
}
Finally, maybe its just late and my brain isn't functioning well, but I'm having a little bit of difficulty converting this data into the Results type, I think because of the multiple layers. Any help?
Edit
I added an answer below for how I ended up accomplishing it, if anyone has a more condensed example let me know!
Well, I don't know what Rows is inside Ga, but maybe this will point you in the right direction.
var results
= GaData.Rows.Select(x => x.Rows.Select(y =>
new Dimension { Value = y.Value, Metrics = new List<Metric> {innerRow.Metric}}));
I ended up creating an extension method for GaData called ToDimensionResults(). I'm not sure if I would have been able to accomplish this using LINQ as I needed to know the index of some of the rows (like the Dimension Value). So I opted to just loop through both dimensions and metrics and create the class manually. NOTE: if you do not include a dimension in your query, the results do not contain the dimension value, only a list of metrics, so this accommodates that possibility.
public static Results ToDimensionResults(this GaData ga)
{
var results = new Results();
var dimensions = new List<Dimension>();
List<Metric> metrics;
var value = "";
var metricStartIndex = 1;
for (var i = 0; i < ga.Rows.Count; i++)
{
//accomodate data without dimensions
if (!string.IsNullOrEmpty(ga.Query.Dimensions))
{
value = ga.Rows[i][0].ToString();
}
else
{
value = "";
metricStartIndex = 0;
}
metrics = new List<Metric>();
for (var x = metricStartIndex; x < ga.Rows[i].Count; x++)
{
metrics.Add(new Metric
{
Value = Convert.ToInt32(ga.Rows[i][x])
});
}
dimensions.Add(new Dimension
{
Value = value,
Metrics = metrics
});
}
results.Dimensions = dimensions;
return results;
}
There's a list of objects, each object representing a record from a database. To sort the records there is a property called SortOrder. Here's a sample object:
public class GroupInfo
{
public int Id { get; set; }
public string Text { get; set; }
public int SortOrder { get; set; }
public GroupInfo()
{
Id = 0;
Text = string.Empty;
SortOrder = 1;
}
}
A list object would look like this:
var list = new List<GroupInfo>();
I need to be able to change the SortOrder and update the SortOrder on the other objects in the list. I figured out how to sort up or down by one. I need to know how to change it by more than one and adjust the SortOrder on the other records. Any ideas?
This could be done by first getting the original SortOrder and the updated SortOrder. You would then iterate through your collection and adjust the SortOrder of any other GroupInfo objects that fall inside the range between original and updated. you could put all of this in a "SetSortOrder" function that takes in the containing collection.
public static void SetSortOrder(List<GroupInfo> groupInfos, GroupInfo target, int newSortOrder)
{
if (newSortOrder == target.SortOrder)
{
return; // No change
}
// If newSortOrder > SortOrder, shift all GroupInfos in that range down
// Otherwise, shift them up
int sortOrderAdjustment = (newSortOrder > target.SortOrder ? -1 : 1);
// Get the range of SortOrders that must be updated
int bottom = Math.Min(newSortOrder, target.SortOrder);
int top = Math.Max(newSortOrder, target.SortOrder);
// Get the GroupInfos that fall within our range
var groupInfosToUpdate = from g in groupInfos
where g.Id != target.Id
&& g.SortOrder >= bottom
&& g.SortOrder <= top
select g;
// Do the updates
foreach (GroupInfo g in groupInfosToUpdate)
{
g.SortOrder += sortOrderAdjustment;
}
target.SortOrder = newSortOrder;
// Uncomment this if you want the list to resort every time you update
// one of its members (not a good idea if you're doing bulk changes)
//groupInfos.Sort((info1, info2) => info1.SortOrder.CompareTo(info2.SortOrder));
}
Update: As suggested, I moved the logic into a static helper function.
var sortedList = list.OrderBy(item => item.SortOrder);
Edit: Sorry, I misunderstood. You will need to write yourself a method outside of GroupInfo to handle the updating of that property.
I just asked a question about my class:
Public class Parent {
public IList<ParentDetail> ParentDetails {
get { return _ParentDetails; }
}
private List<ParentDetail> _ParentDetails = new List<ParentDetail>();
public Parent() {
this._ParentDetails = new List<ParentDetail>();
}
}
public class ParentDetail {
public int Id { get; set; }
}
}
One of the experts here (Jon) told me how I could iterate through this class in order of the ParentDetails.Id. Here's his solution which works good.
Previous question
foreach(var details in Model.Parent.ParentDetails.OrderBy(d => d.Id))
{
// details are processed in increasing order of Id here
// what's needed is to get access to the original order
// information. Something like as follows:
// select index position from ParentDetails where Id = details.ID
}
What I also need is within this foreach to show the index value of the list corresponding to the Id along with some other data that's in the ParentDetail class.
So for example where it says // details are processed then I want to be able to print out the index value that corresponded to the current Id in the foreach loop.
Use the second Enumerable.Select method:
foreach(var details in Model.Parent.ParentDetails
.Select((value, idx) => new { Index = idx, Value = value })
.OrderBy(d => d.Value.Id)
)
{
// details are processed in increasing order of Id here
Console.WriteLine("{0}: {1}", details.Index, details.Value);
}
This is assuming you wanted the indices to be in the original order.
You can use a regular for for that.
var ordered = Model.Parent.ParentDetails.OrderBy(d => d.Id).ToList();
for(int i = 0; i < ordered.Count; i++)
{
// details are processed in increasing order of Id here
}