I am trying to figure out what would be the best / fastest way to accomplish next task.
There is a list of int:
{ 4, 1, 112, 78 }
and there is a list of objects:
object { Id, Date, Value }
Rules:
{int list} contains Ids which are not sorted in any particular order
{int list} contains unknown number of elements
{object list} will always have only one Id occurrence in one particular day. There can be not one date with two same Ids (the list of object is already supplied like this). You could say that Id+Date represents a unique object.
JOIN Part: one day could have 1...n items, where 'n' represents the number of elements in {int list}. Requirement is that in final result all days have 'n' Ids. So if the day 1/1/2014 does not have item with Id=42, then a new item will be added to this list with Value=0.
SORT part: {object list} needs to be sorted by date, and then by Id, but the Id order must be the same as it is in {int list}.
What would be the best algorithm to accomplish this task? This is what I do currently:
// first I insert all the missing Ids
// to achieve this, I sorted lists so I now when to expect which Id
var orderedIntList = intList.OrderBy(x => x).ToList();
var orderedObjectList = objectList.OrderBy(x => x.Date).ThenBy(x => x.Id).ToList();
for (int i = 0; i < totalRecords; i++)
{
currentIndex = i % 4;
currentId = orderedIntList[currentIndex];
if (orderedObjectList.Count <= i || currentId != orderedObjectList[i].Id)
orderedObjectList.Insert(i, new Object { Date = currentDate, Id = currentId });
currentDate = orderedList[i].Date;
}
// then in order to have items sorted in original order, I use LINQ join
int counter = 0;
var aListWithIndex = activityIds.Select(x => new { Index = counter++, Id = x }).ToList();
return (from a in aListWithIndex
join b in orderedObjectList on a.Id equals b.Id
orderby b.Date, a.Index
select b
)
.ToList();
Related
I have below entity structure
public class Item
{
public EnumType Type { get; set; }
public int Price { get; set; }
}
public enum EnumType
{
A =1,
B=2,
C =3
}
I have a list of items as follow
var items = new List<Item>
{
new Item{ Price=5, Type= EnumType.B},
new Item{ Price=5, Type= EnumType.B},
new Item{ Price=5, Type= EnumType.B},
new Item{ Price=10, Type= EnumType.B},
new Item{ Price=10, Type= EnumType.B},
new Item{ Price=10, Type= EnumType.B},
new Item{ Price=15, Type= EnumType.C},
new Item{ Price=15, Type= EnumType.C},
new Item{ Price=15, Type= EnumType.C},
new Item{ Price=15, Type= EnumType.C},
new Item{ Price=15, Type= EnumType.C}
};
If the price and type are same, based on type it need to exclude every nth item from the list and then calculate the sum.
i.e type B = 3, Type C = 4
Which means in above sample data, since there are 3 items each in type B once it group by price and type it need to exclude every 3rd item when calculate sum.
So sum for type B will be 5+5+10+10 and sum for type C will be 15+15+15+15
I tried using modular but seems its not the correct direction
I have tried this so far
static int GetFactorByType(EnumType t)
{
switch(t)
{
case EnumType.A:
return 2;
case EnumType.B:
return 3;
case EnumType.C:
return 4;
default:
return 2;
}
}
var grp = items.GroupBy(g => new { g.Type, g.Price }).Select(s => new
{
type= s.Key.Type,
price = s.Key.Price,
count = s.Count()
}).Where(d => d.count % GetFactorByType(d.type) == 0).ToList();
Here's one solve:
//track the type:nth element discard
var dict = new Dictionary<EnumType, int?>();
dict[EnumType.B] = 3;
dict[EnumType.C] = 4;
//groupby turns our list of items into two collections, depending on whether their type is b or c
var x = items.GroupBy(g => new { g.Type })
.Select(g => new //now project a new collection
{
g.Key.Type, //that has the type
SumPriceWithoutNthElement = //and a sum
//the sum is calculated by reducing the list based on index position: in where(v,i), the i is the index of the item.
//We drop every Nth one, N being determined by a dictioary lookup or 2 if the lookup is null
//we only want list items where (index%N != N-1) is true
g.Where((v, i) => (i % (dict[g.Key.Type]??2)) != ((dict[g.Key.Type] ?? 2) - 1))
.Sum(r => r.Price) //sum the price for the remaining
}
).ToList(); //tolist may not be necessary, i just wanted to look at it
It seemed to me like your question words and your example are not aligned. You said (and did in code):
If the price and type are same, based on type it need to exclude every nth item from the list and then calculate the sum. i.e type B = 3, Type C = 4
Which to me means you should group by Type and Price, so B/5 is one list, and B/10 is another list. But you then said:
Which means in above sample data, since there are 3 items each in type B once it group by price and type it need to exclude every 3rd item when calculate sum. So sum for type B will be 5+5+10+10
I couldn't quite understand this. To me there are 3 items in B/5, so B/5 should be a sum of 10 (B/5 + B/5 + excluded). There are 3 items in B/10, again, should be (B/10 + B/10 + excluded) for a total of 20.
The code above does not group by price. It outputs a collection of 2 items, Type=B,SumWithout=30 and Type=C,SumWithout=60. This one groups by price too, it outputs a 3 item collection:
var x = items.GroupBy(g => new { g.Type, g.Price })
.Select(g => new
{
g.Key.Type,
g.Key.Price,
SumPriceWithoutNthElement =
g.Where((v, i) => (i % (dict[g.Key.Type]??2)) != ((dict[g.Key.Type] ?? 2) - 1))
.Sum(r => r.Price) }
).ToList();
The items are Type=B,Price=5,SumWithout=10 and Type=B,Price=10,SumWithout=20 and Type=C,Price=15,SumWithout=60
Maybe you mean group by type&price, remove every 3rd item (from b, 4th item from c etc), then group again by type only and then sum
This means if your type B prices were
1,1,1,1,2,2,2,2,2
^ ^
we would remove one 1 and one 2 (the Ines with arrows under them), then sum for a total of 9. This is different to removing every 3rd for all type b:
1,1,1,1,2,2,2,2,2
^ ^ ^
?
In which case, maybe group by Type/sum again the SumWithout output from my second example
I did consider that there might be a more efficient ways to do this without LINQ.. and it would nearly certainly be easier to understand the code if if were non LINQ - LINQ isn't necessarily a wonderful magic bullet that can kill all ptoblems, and even though it might look like a hammer with which every problem can be beaten, sometimes it's good to avoid
Depending on how you intended the problem to be solved (is price part of the group key or not) building a dictionary and accumulating 0 instead of th price every Nth element might be one way.. The other way, if price is to be part of the key, could be to sum all the prices and then subtract (count/N)*price from the total price
Grouping by a new object, which is always unique, guarantees you that you'll have as many groups as you have items. Try something like this:
var grp = items.GroupBy(g => $"{g.Type}/{g.Price}").Select(s => new
{
type= s.Value.First().Type,
price = s.Value.First().Price,
count = s.Value.Count()
}).Where(d => count % GetFactorByType(d.type) == 0).ToList();
This way, you group by a string composed from the type/price combination, so if the items are equivalent, the strings will be equal.
The $"{g.Type}/{g.Price}"string amounts to "B/5" for your first three item examples, so it's quite readable as well.
I have list of object of class which contain totalScore as one property.I want to get rank of Team depending upon totalscore of team.Here is the list of object I called it as List data= new List();
so data contain object of scoreboard class with total score property.
I need rank of team depending upon totalscore.Here is the code that I try but it give result like Rank=1,2,2,3 but I need Rank=1,2,2,4 like this.
data.OrderByDescending(x => x.totalScore).GroupBy(x => x.totalScore)
.SelectMany((g, i) => g.Select(e => new { data = e.Rank = i + 1 }))
.ToList();
The data list contain unique team but there total score may be same so same totalscore team must be in one rank. please help me!
If you need to update the list in-place:
int i = 0;
decimal? prevValue = null;
foreach(var item in data.OrderByDescending(x => x.totalScore))
{
item.Rank = prevValue == item.totalScore ? i : ++i;
prevValue = item.totalScore;
}
A different notation (which I prefer for readability) but essentially the same answer as provided by user3185569.
var i = 1;
var results = (from d in data orderby d.totalScore descending select new { Obj = d, Rank = i++ } ).ToList();
I have objects from which measurements are saved to a single table. I want to find out how long an object has been in a certain state within a time period.
So in addition to getting the record with the wanted state I need to pair it up with the next measurement made from the same object to calculate the time between them.
I came up with this monster:
// Get the the object entry from Database
MeasuredObject object1;
try
{
object1 = (MeasuredObject)(from getObject in db.MeasuredObject where wantedObject.Id.Equals(getObject.Id) select getObject).Single();
}
catch (System.ArgumentNullException e)
{
throw new System.ArgumentException("Object does not exist", "wantedObject", e);
}
// Get every measurement which matches the state in the time period and the next measurement from it
var pairs = (from m in object1.Measurements
join nextM in object1.Measurements
on (from next in object1.Measurements where (m.Id < next.Id) select next.Id).Min() equals nextM.Id
where 'm is in time period and has required state'
select new { meas = m, next = nextM });
I would say this doesn't seem very efficient especially when I'm using Compact Edition 3.5.
Is there any way to navigate to the next measurement through m or could I somehow use orderby or group to select next by Id? Or even make the join clause simpler?
From the posted code looks like you are working with in memory collection. If that's true, then the following should be sufficient:
var items = (from m in object1.Measurements
where 'm is in time period and has required state'
orderby m.Id
select m)
.ToList();
var pairs = items.Select((item, index) => new
{
meas = item,
next = index + 1 < items.Count ? items[index + 1] : null
});
EDIT: The above is not the exact equivalent of your code because it applies the filter before pairing the items. The exact optimized equivalent would be like this:
var items = object1.Measurements.OrderBy(m => m.Id).ToList();
var pairs = items.Select((item, index) => new
{
meas = item,
next = index + 1 < items.Count ? items[index + 1] : null
})
.Where(pair => 'pair.meas is in time period and has required state');
I made an SQL query and filled the data to an ObservableCollection. The database contains many columns so I want to count how many instances where a specific column = 1, then return that number to an int.
The query:
var test = from x in m_dcSQL_Connection.Testheaders
where dtStartTime <= x.StartTime && dtEndtime >= x.StartTime
select new {
x.N,
x.StartTime,
x.TestTime,
x.TestStatus,
x.Operator,
x.Login,
x.DUT_id,
x.Tester_id,
x.PrintID
};
Then I add the data pulled from the database to an Observable Collection via:
lstTestData.Add(new clsTestNrData(item.N.ToString(),
item.StartTime.ToString(),
item.TestTime.ToString()
etc.....
I want to count how many times TestStatus = 1.
I have read about the .Count property but I do not fully understand how it works on ObservableCollections.
Any help?
The standard ObservableCollection<T>.Count property will give you the number of items in the collection.
What you are looking for is this:
testStatusOneItemCount = lstTestData.Where(item => item.TestStatus == 1).Count()
...which uses IEnumerable<T>.Count() method which is part of LINQ.
To elaborate a bit, Count will simply count the objects in your collection.
I suggest having a quick look at linq 101. Very good examples.
Here's an example:
// Assuming you have :
var list = new List<int>{1,2,3,4,5,6 };
var items_in_list = list.Count(); // = 6;
Using linq's Where, you're basically filtering out items, creating a new list. So, the following will give you the count of all the numbers which are pair:
var pair = list.Where(item => item%2 ==0);
var pair_count = pair.Count; // = 3
You can combine this without the temp variables:
var total = Enumerable.Range(1,6).Where(x => x % 2 ==0).Count(); // total = 6;
Or you can then select something else:
var squares_of_pairs = Enumerable.Range(1,6)
.Where(x => x % 2 ==0).
.Select( pair => pair*pair);
// squares_of_pairs = {4,16, 36}. You can count them, but still get 3 :)
I've been using 101 LINQ Samples to get my feet wet using LINQ. It's been a good first resource, but I can't see an example there of what I currently need.
I just need to associate a sequential group number with each group. I have a working solution:
var groups =
from c in list
group c by c.Name into details
select new { Name = details.Key, DetailRecords = details };
int groupNumber = 0;
foreach (var group in groups)
{
//
// process each group and it's records ...
//
groupNumber++;
}
But, I'm sure it's possible to use LINQ to also generate the groupNumber. How?
This depends on your exact needs, but you can use:
var groupArray = groups.ToArray();
Similarly, you can use ToList. These data structures are sequential, and each group has an index.
If you do need the index on the object you create, another option is to use Select:
list.GroupBy(c => c.Name)
.Select((details, ind) =>
new
{
Name = details.Key,
DetailRecords = details,
Index = ind
});
this should do the trick:
int groupNumber = 0;
var groups =
from c in list
group c by c.Name into details
select new { Name = details.Key, DetailRecords = details, grpNum = groupNumber++};
if it's just a sequential group number, just use the Count() method on your IEnumerable.
var groups =
from c in list
group c by c.Name into details
select new {Name = details.Key, DetailRecords = details};
for(int i = 0; i < groups.Count(); i++)
{
//Process Records
}
Then, if you need the specific group number, you can just grab i.