Select from mulitle tables with count in Linq - linq

I am busy with a small online voting web app, now I struggling to get the total number of votes for each party that I stored in a different table. Here is what I have tried, this method gets each party from the votes table named [dbo].[VoterCandidateMapping]
public List<int> GetAllPartIDs()
{
List<int> partieIDs = new List<int>();
var parties = (from votes in voteDB.VoterCandidateMappings
select votes.PartyID).Distinct().ToList();
partieIDs = parties;
return partieIDs;
}
Then I want to use this method to count each vote associated with a particular part, here is the code
public IQueryable<ResultsViewModel> GetResults()
{
int numberOfVotes = 0;
foreach (int IDs in GetAllPartIDs())
{
numberOfVotes = (from votes in voteDB.VoterCandidateMappings
where votes.PartyID == IDs ? true : false
select votes.VoterID).Count();
}
return (
from results in voteDB.VoterCandidateMappings
join parties in voteDB.Parties
on results.PartyID equals parties.Id
select new ResultsViewModel
{
PartyName = parties.Name,
TotalVotes = numberOfVotes
});
}
It runs and return almost every data but the total number of votes is the same

The reason why it does not work is that you are trying to store multiple values in a single numberOfVotes variable.
Let's go through code what you have now.
First foreach loop calculate votes for each party and assigns to numberOfVotes variable. Each time value is assigned, existing value in numberOfVotes is overwritten. In the end of loop numberOfVotes contains number of votes for the last party. This is value you are seeing in your results as you use the same variable to return results.
Here is one way to do it correctly:
public IQueryable<ResultsViewModel> GetResults()
{
var groupedVotes = voteDB.VoterCandidateMappings
.GroupBy(x => x.PartyID)
.Select(x => new { PartyId = x.Key, NumberOfVotes = x.Count());
return voteDB.Parties
.Select(x => new ResultsViewModel
{
PartyName = x.Name,
TotalVotes = groupedVotes
.Where(y => y.PartyId == x.Id)
.Select(y => y.NumberOfVotes)
.FirstOrDefault()
});
}

Related

Find / Count Redundant Records in a List<T>

I am looking for a way to identify duplicate records...only I want / expect to see them.
So the records aren't duplicated completely but the unique fields I am unconcerned with at this point. I just want to see if they have made X# payments of the exact same amount, via the exact same card, to the exact same person. (Bogus example just to illustrate)
The collection is a List<> further whatever X# is the List<>.Count will be X#. In other words all the records in the list match (again just the fields I am concerned with) or I will reject it.
The best I can come up with is to take the first record get value of say PayAmount and LINQ the other two to see if they have the same PayAmount value. Repeat for all fields to be matched. This seems horribly inefficient but I am not smart enough to think of a better way.
So any thoughts, ideas, pointers would be greatly appreciated.
JB
Something like this should do it.
var duplicates = list.GroupBy(x => new { x.Amount, x.CardNumber, x.PersonName })
.Where(x => x.Count() > 1);
Working example:
class Program
{
static void Main(string[] args)
{
List<Entry> table = new List<Entry>();
var dup1 = new Entry
{
Name = "David",
CardNumber = 123456789,
PaymentAmount = 70.00M
};
var dup2 = new Entry
{
Name = "Daniel",
CardNumber = 987654321,
PaymentAmount = 45.00M
};
//3 duplicates
table.Add(dup1);
table.Add(dup1);
table.Add(dup1);
//2 duplicates
table.Add(dup2);
table.Add(dup2);
//Find duplicates query
var query = from p in table
group p by new { p.Name, p.CardNumber, p.PaymentAmount } into g
where g.Count() > 1
select new
{
name = g.Key.Name,
cardNumber = g.Key.CardNumber,
amount = g.Key.PaymentAmount,
count = g.Count()
};
foreach (var item in query)
{
Console.WriteLine("{0}, {1}, {2}, {3}", item.name, item.cardNumber, item.amount, item.count);
}
Console.ReadKey();
}
}
public class Entry
{
public string Name { get; set; }
public int CardNumber { get; set; }
public decimal PaymentAmount { get; set; }
}
The meat of which is this:
var query = from p in table
group p by new { p.Name, p.CardNumber, p.PaymentAmount } into g
where g.Count() > 1
select new
{
name = g.Key.Name,
cardNumber = g.Key.CardNumber,
amount = g.Key.PaymentAmount,
count = g.Count()
};
You're unique entries are based off of the 3 criteria of Name, Card Number, and Payment Amount so you group by them and then use .Count() to count how many of those unique values exist. where g.Count() > 1 filters the group to duplicates only.

Linq GroupBy to alter list

I have a list of items in an IList<>.Each listitem has a date and a few other fields.
I need to order the list by date and then change the list to only show a date for the first item and effectively set the date field to null for the other items if the date is repeated.
Example:
12/01/2012 500
12/01/2012 700
15/02/2012 900
15/02/2012 1100
27/05/2012 2000
Desired Result:
12/01/2012 500
null 700
15/02/2012 900
null 1100
27/05/2012 2000
Is this possible with the linq group by and order by?
Thanks
LINQ operators are not supposed to change the underlying data. You'd better use regular foreach if you're going to modify the data.
This should probably work:
var groups = items.GroupBy(x => x.Date).ToArray();
foreach (var group in groups)
{
foreach (var item in group.Skip(1)) item.Date = null;
}
I would avoid using such a construction since you'll have to double-check that GroupBy preserves order. Instead I would use something like this:
var sortedItems = items.OrderBy(x => x.Date);
var lastVisitedDate = (DateTime?) null;
foreach (var item in sortedItems)
if (Equals(item.Date, lastVisitedDate)) item.Date = null;
else lastVisitedDate = item.Date;
This should work:
var list = new List<DateItem>();
// Initialization ...
var dups = list.Select((Item,Index) => new{ Item,Index })
.GroupBy(x => x.Item.Date)
.Where(g => g.Count() > 1);
foreach(var dup in dups)
{
foreach (var nullable in dup.OrderBy(x => x.Item.Date).Skip(1))
{
list[nullable.Index].Date = null;
}
}
Assuming your class looks similar to this:
class DateItem {
public DateTime? Date;
public int OtherField;
}
Edit: Here's a working demo: http://ideone.com/cVL4G
One way is to use LINQ to get all of the followers and then set their dates to null in a loop:
// Use ToList() to make sortedItems non-lazy so it won't get ordered each time it's called.
var sortedItems = items.OrderBy(x => x.Date).ToList();
var followers = sortedItems.GroupBy(item => item.Date)
.SelectMany(group => group.Skip(1));
foreach (var follower in followers)
{
follower.Date = null;
}
// Now you can use sortedItems.
Or if you prefer the query syntax:
var followers = from item in sortedItems
group item by item.Date into grp
from follower in grp.Skip(1)
select follower;

how to select collection type navigation property's value

I have 3 tables, team(id,name) player(id,teamid,name) playerdetail(id,playerid,height,weight), the relationship between team and player is one to many, the relationship between player and playerdetail is one to one.
I want to use eager loading to load all the information and print out the name of players who is higher than 2 meters.
I have write the code below,
using (var context = new TestEntities())
{
var query = from t in context.Teams.Include("Players.PlayerDetails") select t;
foreach (var v in query)
{
Console.WriteLine(v.Players.Any(x => x.PlayerDetails.Any(y => y.Height > 200)));
}
Console.Read();
}
It prints out only true and false, how can I modify it and make it print out the name of player?
Thanks in advance
Why don't you just query the players through context.Players like below?
using (var context = new TestEntities())
{
var query = context.Players.Include("Team").Include("PlayerDetails")
.Where(p => p.Height > 200);
foreach (var v in query)
{
Console.WriteLine(v.Name);
}
Console.Read();
}

EF Code First selecting rows based on many to many relationship

I have the following code in my repository:
public PagedResult<Post> GetAllPublishedByTag(int tagId, int start, int max)
{
var query = Database.Set<Post>().Where(p => p.IsPublished)
.OrderByDescending(p => p.CreatedAt)
.Skip(start)
.Take(max);
int total = query.Count();
var result = query.ToList();
return new PagedResult<Post>(result, total);
}
This will give me all published posts. But what I want is selecting all published posts for a certain tag. My model is setup in such a way that tags have a many to many relationship to posts. I tried to slightly modify the above code but this did not work:
public PagedResult<Post> GetAllPublishedByTag(Tag tag, int start, int max)
{
var query = Database.Set<Post>().Where(p => p.Tags.Contains(tag) && p.IsPublished)
.OrderByDescending(p => p.CreatedAt)
.Skip(start)
.Take(max);
int total = query.Count();
var result = query.ToList();
return new PagedResult<Post>(result, total);
}
I would prefer to pass in the tagId (as per the first code example) as opposed to the tag object but not sure how to correctly write the LINQ statement.
var query = Database.Set<Post>().Where(p => p.Tags.Any(t => t.Id == tagId) && p.IsPublished)
.OrderByDescending(p => p.CreatedAt)
.Skip(start)
.Take(max);
Side Note: I believe you may have issues with your pagination, as the variable total is calculated after skip/take are called.

How to select decreasing sub-series with Linq

I have a list of prices ordered by date. I need to select all monotonously decreasing values. The following code works:
public static List<DataPoint> SelectDecreasingValues(List<DataPoint> dataPoints)
{
var ret = new List<DataPoint>(dataPoints.Count);
var previousPrice = dataPoints[0].Price;
for (int i = 0; i < dataPoints.Count; i++)
{
if (dataPoints[i].Price <= previousPrice)
{
ret.Add(dataPoints[i]);
previousPrice = dataPoints[i].Price;
}
}
return ret;
}
However, is there a shorter/cleaner way to accomplish it with Linq?
This code is equivalent:
previousPrice = dataPoints[0].Price;
var ret = dataPoints.Where(x => {
if(x.Price <= previousPrice)
{ previousPrice = x.Price; return true;}
return false;
}).ToList();
However, if you don't need to have a list, go with plain enumerables and drop the ToList at the end. That way you can make use of the deferred execution feature built into LINQ.
The following code is also equivalent:
DataPoint previous = dataPoints.FirstOrDefault();
var ret = dataPoints.Where(x => x.Price <= previous.Price)
.Select(x => previous = x).ToList();
This works because of the deferred execution in LINQ. For each item in dataPoints it will first execute the Where part and then the Select part and only then will it move to the second item in dataPoints.
You need to decide which version you want to use. The second one is not as intention revealing as the first one, because you need to know about the internal workings of LINQ.
public IEnumerable<T> WhereMonotonicDecreasing<T>(
IEnumerable<T> source,
Func<T, IComparable> keySelector)
{
IComparable key;
bool first = true;
foreach(T t in source)
{
if (first)
{
key = keySelector(t);
yield return t;
first = false;
}
else
{
IComparable newKey = keySelector(t);
if (newKey.CompareTo(key) < 0)
{
key = newKey;
yield return t;
}
}
}
}
Called by:
dataPoints.WhereMonotonicDecreasing(x => x.Price);
previousPrice = dataPoints[0];
dataPoints.Where(p => p.Price <= previousPrice.Price)
.Select(p => previousPrice = p);
You can then use .ToList() if you really need one.
How about (untested):
return dataPoints.Take(1)
.Concat(dataPoints.Skip(1)
.Zip(dataPoints,
(next, previous) =>
new { Next = next, Previous = previous })
.Where(a => a.Next.Price <= a.Previous.Price)
.Select(a => a.Next))
.ToList();
Essentially, this overlays a "one-deferred" version of the sequence over the sequence to produce "next, previous" tuples and then applies the relevant filters on those tuples. The Take(1) is to pick the first item of the sequence, which it appears you always want.
If you don't care for the readability of the variable names, you could shorten it to:
return dataPoints.Take(1)
.Concat(dataPoints.Skip(1)
.Zip(dataPoints, Tuple.Create)
.Where(a => a.Item1.Price <= a.Item2.Price)
.Select(a => a.Item1))
.ToList();

Resources