I want to find a specific value from a List with the method select.
My code :
public class Calc
{
public int IdCalc { get; set; }
public double Result { get; set; }
public int Number { get; set; }
}
public class Program
{
static void Main()
{
Calc myC1 = new Calc();
List<Calc> liCalc = new List<Calc>();
myC1.IdCalc = -1;
myC1.Result = 20.2;
myC1.Number = 1;
Calc myC2 = new Calc();
myC2.IdCalc = 22;
myC2.Result = 20.2;
myC2.Number = 2;
liCalc.Add(myC1);
liCalc.Add(myC2);
double getResult = ((Calc)(liCalc.Select(Calc => Calc.IdCalc = 22 && Calc.Number = 2))).Result;
Console.ReadKey();
}
}
As you can see my List contains two objects: myC1 and myC2.
I just want to find the value of Result when IdCalc = 22 and Number = 2 thats why I tried to use Select but it's not working with two parameters.
You could use Where, which lets you filter results based on some criteria, however that will return an IEnumerable<Calc>. Since you are only looking for a single result, you should use First which also takes a predicate and only returns the first Calc:
Calc myCalc = liCalc.First(c => c.IdCalc == 22 && c.Number == 2);
double result = myCalc.Result;
This will throw an exception if there is nothing that matches the filter, though. If you're worried about that, use FirstOrDefault which will return null if there is no match.
public class Calc
{
public int IdCalc { get; set; }
public double Result { get; set; }
public int Number { get; set; }
}
public class Program
{
static void Main()
{
Calc myC1 = new Calc();
List<Calc> liCalc = new List<Calc>();
myC1.IdCalc = -1;
myC1.Result = 20.2;
myC1.Number = 1;
Calc myC2 = new Calc();
myC2.IdCalc = 22;
myC2.Result = 20.2;
myC2.Number = 2;
liCalc.Add(myC1);
liCalc.Add(myC2);
double getResult = liCalc.First(item => item.IdCalc == 22 && item.Number == 2).Result; //Note that this will throw an exception if no item in the list satisfies the condition.
Console.ReadKey();
}
You could use the following statement
double getResult = liCalc.Where(Calc => Calc.IdCalc = 22 && Calc.Number = 2))).Select(y=>y.Result).FirstOrDefault();
Essentially using Where() followed by Select().
Related
myClass structure :
public class myClass
{
public string Name { get; set; }
public string AdditionalData { get; set; }
public System.DateTime ActivityTime { get; set; }
}
I have a list of the above class List all ordered by ActivityTime say 'acts'.
I wish to convert my data to a list of following class..
public class newClass
{
public string Name { get; set; }
public string AdditionalData { get; set; }
public System.DateTime StartTime { get; set; }
public System.DateTime EndTime { get; set; }
}
Here StartTime will have the same value as the prev class's ActivityTime and so I do not have a problem.
But EndTime should have the ActivityTime value of next list object, this I'm unable to figure how to get..
The last list object's EndTime can be same as starttime
so my code is ..
List<newClass> items = new List<newClass>();
foreach (var item in acts)
{
newClass si = new newClass
{
Name=item.Name,
AdditionalData=item.AdditionalData,
StartTime = item.ActivityTime ,
EndTime = //what do I do here??????
};
items.Add(si);
}
Any help is sincerely appreciated
It's not possible to access the next iterator value until moving to that position.
What you can do here is to remember the previous item and update it in the next loop. Assuming that for the last element it should be empty it will look like this:
List<newClass> items = new List<newClass>();
newClass last = null;
foreach (var item in acts) {
// update the last element here:
if (last != null)
last.EndTime = item.ActivityTime;
newClass si = new newClass
{
Name=item.Name,
AdditionalData=item.AdditionalData,
StartTime = item.ActivityTime ,
//EndTime = null; // will be updated in the next loop
};
last = si;
items.Add(si);
}
// handle the last item (if needed):
if (last != null)
last.EndTime = ... // special value for last item
I have a much elegant solution than for-loop:
List<newClass> output = acts.Select((a, index) => new newClass()
{
Name = a.Name,
AdditionalData = a.AdditionalData,
StartTime = a.ActivityTime,
EndTime = (index + 1 < acts.Count) ? acts[index + 1].ActivityTime : default(DateTime)
}).ToList();
Or you can use a for loop instead of foreach:
List<newClass> items = new List<newClass>();
// assuming length > 0
int length = (acts.Length % 2 == 0) ? acts.Length : acts.Length - 1;
for (int i = 0; i < acts.Length; i++)
{
newClass si = new newClass
{
Name=acts[i].Name,
AdditionalData=acts[i].AdditionalData,
StartTime = acts[i].ActivityTime ,
EndTime = acts[i+1].ActivityTime
};
items.Add(si);
}
if (length < acts.Length)
// handle the last element as you wish
If you want to use LINQ you can create an extension method such as...
public static class EnumerableEx
{
public static IEnumerable<Tuple<T, T>> WithNext<T>(this IEnumerable<T> items)
{
var e = items.GetEnumerator();
bool r = e.MoveNext();
if (!r)
yield break;
do
{
T last = e.Current;
var item = (r = e.MoveNext()) ? e.Current : default(T);
yield return Tuple.Create(last, item);
} while (r);
}
}
... here is an example on how to use it ...
class Program
{
static void Main(string[] args)
{
var i = new int?[] { 1, 2, 3 };
foreach (var n in i.WithNext())
//the last value will be paired with a null.
// you can use null coalesce to fill in the missing item.
Console.WriteLine("{0} => {1}", n.Item1, n.Item2 ?? 9);
/*
1 => 2
2 => 3
3 => 9
*/
}
}
I have a class Guest
class Guest
{
bool f = true;
bool g = false;
bool s = false;
string name = "";
}
And a List where all the included -> var g = new List<Guest>();
It can be only one of the booleans true.
At the start come all the f guest then g guest in the middle and at last the s guest.
But all guest must be sorted alphabetic in f or g or in the a group.
Maybe so?
var query = (from Guest in GuestList
orderby Guest.f, Guest.g, Guest.s, Guest.name
select Guest);
I'm just not it.
Thanks and greetz, Malte
Sounds like pretty typical nested sorting. There is no need to group.
var result = source
.OrderBy(guest =>
guest.f ? 1 :
guest.g ? 2 :
guest.s ? 3 :
4)
.ThenBy(guest => guest.name);
For those who are unfamiliar with the syntax, allow me to read the code.
In the call to OrderBy, there is a lambda function which uses a chained ternary operator to generate a sort key for each row. OrderBy sorts by this sort key.
The result of OrderBy is an IOrderedEnumerable<Guest> and is passed to ThenBy.
ThenBy preserves the sorting of the prior ordering operations and works to break ties.
This should work. The Sort and OrderBy will use the CopmpareTo() method
public class Guest : IComparable<Guest>
{
public bool f { get; set; }
public bool g { get; set; }
public bool s { get; set; }
public string name { get; set; }
public int CompareTo(Guest other)
{
int results = 0;
if (this.f)
{
if (other.f)
{
results = this.name.CompareTo(other.name);
}
else
{
results = 1;
}
}
else
{
if (other.f)
{
results = -1;
}
else
{
if (this.g)
{
if (other.g)
{
results = this.name.CompareTo(other.name);
}
else
{
results = 1;
}
}
else
{
if (other.g)
{
results = -1;
}
else
{
if (this.s)
{
if (other.s)
{
results = this.name.CompareTo(other.name);
}
else
{
results = 1;
}
}
else
{
results = this.name.CompareTo(other.name);
}
}
}
}
}
return results;
}
Below is simpler method which will even work with when more than one property is true. Notice that I used 1,2,4 instead of 1,2,3 as in other solutions. 1,2,3 has issue that there is more than one way of getting 3 when multiple properties are true.
public class Guest : IComparable<Guest>
{
public bool f { get; set; }
public bool g { get; set; }
public bool s { get; set; }
public string name { get; set; }
public int CompareTo(Guest other)
{
int results = 0;
int thisRank = (this.f ? 1 : 0) + (this.g ? 2 : 0) + (this.s ? 4 : 0);
int otherRank = (other.f ? 1 : 0) + (other.g ? 2 : 0) + (other.s ? 4 : 0);
if (thisRank == otherRank)
{
results = this.name.CompareTo(other.name);
}
else
{
results = thisRank.CompareTo(otherRank);
}
return results;
}
}
here is David B's example with more common syntax for the if statements.
var result = source
.OrderBy(guest => { if (guest.f == true) return 1 else
if (guest.g == true) return 2 else
if (guest.s == true) return 3 else return 4;})
.ThenBy(guest => guest.name);
I have a list of dates (currently there are 4000 in the selected result)
I am trying to put the results in a chart,
The Class looks like this
public class DisplayObjectDates
{
public int Month { get; set; }
public int Year { get; set; }
public int day { get; set; }
public DateTime fulldatetime { get; set; }
public int CountedDate { get; set; }
}
I have a list of the class
private static List<DisplayObjectDates> SortedDatesDays = new List<DisplayObjectDates>();
and I add to the list like this after calling from EF and getting a returned list
if (SortedDatesDays.Count() == 0)
{
var addDisplayObjectDatesYear = new DisplayObjectDates();
addDisplayObjectDatesYear.Year = contextreturned.change_time.Year;
addDisplayObjectDatesYear.Month = contextreturned.change_time.Month;
addDisplayObjectDatesYear.day = contextreturned.change_time.Day;
addDisplayObjectDatesYear.fulldatetime = contextreturned.change_time;
addDisplayObjectDatesYear.CountedDate = 1;
SortedDatesDays.Add(addDisplayObjectDatesYear);
}
else
{
foreach (var VARIABLE in SortedDatesDays)
{
if (VARIABLE.day == contextreturned.change_time.Day && VARIABLE.Month == contextreturned.change_time.Month && VARIABLE.Year == contextreturned.change_time.Year)
{
VARIABLE.CountedDate = VARIABLE.CountedDate++;
}
else
{
var addDisplayObjectDatesYear = new DisplayObjectDates();
addDisplayObjectDatesYear.Year = contextreturned.change_time.Year;
addDisplayObjectDatesYear.Month = contextreturned.change_time.Month;
addDisplayObjectDatesYear.day = contextreturned.change_time.Day;
addDisplayObjectDatesYear.fulldatetime = contextreturned.change_time;
addDisplayObjectDatesYear.CountedDate = 1;
SortedDatesDays.Add(addDisplayObjectDatesYear);
}
}
}
This gives me an error
Collection was modified; enumeration operation may not execute.
so I change the
foreach (var VARIABLE in SortedDatesDays)
to
foreach (var VARIABLE in SortedDatesDays.ToList())
and now i get out of memory exeption
At the end of the day I need to count how many times an event happened on a certain date
I need to put this into a form that i can then use in a chart (DEVEXPRESS)
I am not sure if i should use linq or the current system.
All out of ideas any help with the corrent way in doing this would be greatly appreciated
thanks
a0011010011
I actually thought that foreach (var VARIABLE in SortedDatesDays.ToArray()) will solve the problem as I use it often.
In that case, try the following.
...
else
{
// create a temporary collection for storing new items
var list = new List<DisplayObjectDates>();
foreach (var VARIABLE in SortedDatesDays)
{
if (...) { ... }
else
{
var addDisplayObjectDatesYear = new DisplayObjectDates();
...
// place it to the new list instead
list.Add(addDisplayObjectDatesYear);
}
}
// merge lists
SortedDatesDays.AddRange(list);
}
While you are assigning FullDateTime DisplayObjectDates object ... I prefer to say you to reconstruct your class it may be something like this ... it also help your program to use less memery ..
public class DisplayObjectDates
{
public int Month { get { return fulldatetime.Month; } }
public int Year { get { return fulldatetime.Year; } }
public int day { get { return fulldatetime.Day; } }
public DateTime fulldatetime { get; set; }
public int CountedDate { get; set; }
}
Then sort your initializing function
if (SortedDatesDays.Count() == 0)
{
var addDisplayObjectDatesYear = new DisplayObjectDates();
addDisplayObjectDatesYear.fulldatetime = contextreturned.change_time;
addDisplayObjectDatesYear.CountedDate = 1;
SortedDatesDays.Add(addDisplayObjectDatesYear);
}
else
{
foreach (var VARIABLE in SortedDatesDays)
{
if (VARIABLE.fulldatetime.Date == contextreturned.change_time.fulldatetime.Date)
{
VARIABLE.CountedDate = VARIABLE.CountedDate++;
}
else
{
var addDisplayObjectDatesYear = new DisplayObjectDates();
addDisplayObjectDatesYear.fulldatetime = contextreturned.change_time;
addDisplayObjectDatesYear.CountedDate = 1;
SortedDatesDays.Add(addDisplayObjectDatesYear);
}
}
}
I am trying to create a simple 'inventory' system that stores items with the key being an items name, and with the remaining information being stored as a value. However, I am having difficulty figuring out how to then read the information. For example, if I have say a list of 10 items, and I want to select the items 'type' information from the key 'television' outlined below, how could I do this?
television {large, 5, 3, false, dynamic, 0.8, 20}
Hashtable myItems = new Hashtable();
protected virtual bool OnAttempt_AddItem(object args) {
object[] arr = (object[])args;
string ItemType = (string)arr[0];
string ItemName = (string)arr[1];
int ItemAmount = (arr.Length == 2) ? (int)arr[2] : 1;
int ItemACanHave = (arr.Length == 3) ? (int)arr[3] : 1;
bool ItemClear = (bool)arr[4];
string ItemEffect = (string)arr[5];
float ItemModifier = (float)arr[6];
int ItemWeight = (int)arr[7];
// enforce ability to have atleast 1 item of each type
ItemACanHave = Mathf.Max(1, ItemACanHave);
myItems[ItemName] = new object[] {ItemType, ItemAmount, ItemACanHave, ItemClear, ItemEffect, ItemModifier, ItemWeight };
return true;
}
Create an item class to encapsulate the properties:
public class InventoryItem
{
public string Name;
public string Type;
public int Amount;
public int CanHave; // you should consider renaming this - it's very unclear what this could mean
public bool Clear;
public string Effect;
public float Modifier;
public int Weight;
}
Then you can use a Dictionary to store items:
Dictionary<string, InventoryItem> inventory = new Dictionary<string, InventoryItem>();
inventory["television"] = new InventoryItem
{
Name = "television", Type = "large", Amount = 5,
CanHave = 3, Clear = false, Effect = "dynamic",
Modifier = 0.8, Weight = 20
});
And you can look it up like this:
Console.WriteLine("Type of television is: ", inventory["television"].Type);
I would suggest you to consider the possibility of more than one item of a certain type in a inventory list, i.e. two or more television sets instead of only one.
Use a base class and derived classes:
public class InventoryItem
{
public string ItemType { get; set; }
public string ItemName { get; set; }
public int ItemAmount { get; set; }
public int ItemACanHave { get; set; }
public bool ItemClear { get; set; }
public string ItemEffect { get; set; }
public float ItemModifier { get; set; }
public int ItemWeight { get; set; }
}
public class Radio : InventoryItem
{
}
public class Television : InventoryItem
{
}
// TODO: add your derived classes
Use a List<InventoryItem> to store the collection:
List<InventoryItem> InventoryItems = new List<InventoryItem>();
Modify your method (don't forget to add exception handling, as sometimes you might get different input than the one you expected in the args object):
protected virtual bool OnAttempt_AddItem(object args)
{
// TODO: handle unboxing exceptions, size of the array etc
//
try
{
object[] arr = (object[])args;
switch (arr[0].ToString().ToLower())
{
// TODO: add other types (Radio etc)
case "television":
var tv = new Television();
tv.ItemType = (string)arr[0];
tv.ItemName = (string)arr[1];
tv.ItemAmount = (arr.Length == 2) ? (int)arr[2] : 1;
tv.ItemACanHave = (arr.Length == 3) ? (int)arr[3] : 1;
tv.ItemClear = (bool)arr[4];
tv.ItemEffect = (string)arr[5];
tv.ItemModifier = (float)arr[6];
tv.ItemWeight = (int)arr[7];
// enforce ability to have atleast 1 item of each type
tv.ItemACanHave = Math.Max(1, tv.ItemACanHave);
InventoryItems.Add(tv);
break;
default:
var genericItem = new InventoryItem();
genericItem.ItemType = (string)arr[0];
genericItem.ItemName = (string)arr[1];
genericItem.ItemAmount = (arr.Length == 2) ? (int)arr[2] : 1;
genericItem.ItemACanHave = (arr.Length == 3) ? (int)arr[3] : 1;
genericItem.ItemClear = (bool)arr[4];
genericItem.ItemEffect = (string)arr[5];
genericItem.ItemModifier = (float)arr[6];
genericItem.ItemWeight = (int)arr[7];
// enforce ability to have atleast 1 item of each type
genericItem.ItemACanHave = Math.Max(1, genericItem.ItemACanHave);
InventoryItems.Add(genericItem);
break;
//handle other cases
}
return true;
}
catch (Exception ex)
{
// log the error
return false;
}
}
Retrieve the filtered items like this:
var largeTvType = inventory.InventoryItems.OfType<Television>()
// filter by type (or other criteria)
.Where(tv => tv.ItemType == "large")
// select only the property your interested in (in the case below
// it will be always "television" because that's part of the
// logic inside the OnAttempt_AddItem method's switch statement)
.Select(tv => tv.ItemType);
Still, as ChrisWue suggested in his answer, if you know that your inventory lists will be very large, I'd recommend you to use a Dictionary<string, InventoryItem>, the string key being a unique inventory item identifier. It will be faster.
I have a function that uses LINQ to get data from the database and then I call that function in another function to sum all the individual properties using .Sum() on each individual property. I was wondering if there is an efficient way to sum all the properties at once rather than calling .Sum() on each individual property. I think the way I am doing as of right now, is very slow (although untested).
public OminitureStats GetAvgOmnitureData(int? fnsId, int dateRange)
{
IQueryable<OminitureStats> query = GetOmnitureDataAsQueryable(fnsId, dateRange);
int pageViews = query.Sum(q => q.PageViews);
int monthlyUniqueVisitors = query.Sum(q => q.MonthlyUniqueVisitors);
int visits = query.Sum(q => q.Visits);
double pagesPerVisit = (double)query.Sum(q => q.PagesPerVisit);
double bounceRate = (double)query.Sum(q => q.BounceRate);
return new OminitureStats(pageViews, monthlyUniqueVisitors, visits, bounceRate, pagesPerVisit);
}
private IQueryable<OminitureStats> GetOmnitureDataAsQueryable(int? fnsId, int dateRange)
{
var yesterday = DateTime.Today.AddDays(-1);
var nDays = yesterday.AddDays(-dateRange);
if (fnsId.HasValue)
{
IQueryable<OminitureStats> query = from o in lhDB.omniture_stats
where o.fns_id == fnsId
&& o.date <= yesterday
&& o.date > nDays
select new OminitureStats (
o.page_views.GetValueOrDefault(),
o.monthly_unique.GetValueOrDefault(),
o.visits.GetValueOrDefault(),
(double)o.bounce_rate.GetValueOrDefault()
);
return query;
}
return null;
}
public class OminitureStats
{
public OminitureStats(int PageViews, int MonthlyUniqueVisitors, int Visits, double BounceRate)
{
this.PageViews = PageViews;
this.MonthlyUniqueVisitors = MonthlyUniqueVisitors;
this.Visits = Visits;
this.BounceRate = BounceRate;
this.PagesPerVisit = Math.Round((double)(PageViews / Visits), 1);
}
public OminitureStats(int PageViews, int MonthlyUniqueVisitors, int Visits, double BounceRate, double PagesPerVisit)
{
this.PageViews = PageViews;
this.MonthlyUniqueVisitors = MonthlyUniqueVisitors;
this.Visits = Visits;
this.BounceRate = BounceRate;
this.PagesPerVisit = PagesPerVisit;
}
public int PageViews { get; set; }
public int MonthlyUniqueVisitors { get; set; }
public int Visits { get; set; }
public double PagesPerVisit { get; set; }
public double BounceRate { get; set; }
}
IIRC you can do all the sums in one go (as long as the query is translated to SQL) with
var sums = query.GroupBy(q => 1)
.Select(g => new
{
PageViews = g.Sum(q => q.PageViews),
Visits = g.Sum(q => q.Visits),
// etc etc
})
.Single();
This will give you one object which contains all the sums as separate properties.
I found out why it was throwing the NotSupportedException. I learned that Linq to Entity does not support constructors with parameters, So deleted the constructors and made changes in my query. I am a novice C# programmer, so let me know if my solution could be improved, but as of right now it is working fine.
public class OminitureStats
{
public int PageViews { get; set; }
public int MonthlyUniqueVisitors { get; set; }
public int Visits { get; set; }
public double PagesPerVisit { get; set; }
public double BounceRate { get; set; }
}
private IQueryable<OminitureStats> GetOmnitureDataAsQueryable(int? fnsId, int dateRange)
{
var yesterday = DateTime.Today.AddDays(-1);
var nDays = yesterday.AddDays(-dateRange);
if (fnsId.HasValue)
{
IQueryable<OminitureStats> query = from o in lhDB.omniture_stats
where o.fns_id == fnsId
&& o.date <= yesterday
&& o.date > nDays
select new OminitureStats() {
o.page_views.GetValueOrDefault(),
o.monthly_unique.GetValueOrDefault(),
o.visits.GetValueOrDefault(),
(double)o.bounce_rate.GetValueOrDefault()
};
return query;
}
return null;
}