mohammad
mohammad

Reputation: 1026

LINQ query computing vote count

I have a Items table and ItemVotes table that every user can give Up vote or Down vote to an item and his/her vote determine by a bool property in ItemVotes table. Now I want to have a list of Items order by votes, for do this we need to get count of true votes minus count of false votes.

_ItemsService.GetAll(x => x.ItemVotes //here I have to order by vote)

UPDATE :

Items Entity:

public class Items : Entity<int>
{
    public int UserId { get; set; }
    public DateTime Date { get; set; }
    public string Subject { get; set; }

    public virtual ICollection<ItemVotes> ItemVotes { get; set; }
}

ItemVotes Entity:

public class ItemVotes : Entity<int>
{
    public int ItemId { get; set; }
    public int UserId { get; set; }
    public DateTime Date { get; set; }
    public bool TypeVote { get; set; }

    public virtual Items Items { get; set; }
    public virtual Users Users { get; set; }
}

UPDATE 2:

I use Gilad Green's answer in an action like this:

public ActionResult TopItems()
    {
        var items = _ItemsService.GetAllQuerable(x => x)
            .OrderBy(item => item.ItemsVote.Sum(vote => vote.TypeVote ? 1 : -1));
        return View(items);
    }

enter image description here

And here is GetAllQuerable() function :

 public IEnumerable<T> GetAllQuerable(
        Expression<Func<T,
        bool>> filter = null,
        Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
        string includeProperties = "")
    {
        return _repository.GetAllQuerable(filter, orderBy, includeProperties);
    }

Upvotes: 0

Views: 183

Answers (4)

Gilad Green
Gilad Green

Reputation: 37281

To achieve I want to have a list of Items order by votes first retrieve all items and then order them by the described logic:

retrievedItems.OrderBy(item => item.ItemVotes.Sum(vote => vote.TypeVote ? 1 : -1));

In your current code of _ItemsService.GetAll(x => x.ItemVotes)... you lose the scope of knowing which vote belongs to which item and hence will get a single summary for all items together

Upvotes: 1

Sudipto
Sudipto

Reputation: 41

You need to do the following in order to get a sorted list of items based on their votes with most positive voted items first.

Expose a method GetOrderedItems in your _ItemsService which will perform all the logic and return you the desired result instead of running your logic inside the caller to this service.

GetOrderedItems(Func<bool,int> predicate)
{
 return items.OrderByDescending(p => p.ItemVotes.Select(x => x.TypeVote).Sum(predicate)).ToList();        
}

In your call to the service you can pass in the selector like this:

Func<bool, int> getPositiveVotedItemsFirst = (vote) => vote ? 1 : -1;

var itemsOrderedByPositiveVotes = _ItemsService.GetOrderedItems(getHighlyVotedItems);

You could now easily get the items ordered by the least voted ones , just by changing your selector.

Func<bool, int> getLeastVotedItemsFirst = (vote) => !vote ? 1 : -1;

var itemsOrderedByNegativeVotes = _ItemsService.GetOrderedItems(getLeastVotedItemsFirst);

Upvotes: 0

Lajos Arpad
Lajos Arpad

Reputation: 76988

Let's suppose you have a variable foo where you have the items, then:

bar = foo.OrderBy(item => item.ItemVotes.Sum(vote => vote.TypeVote ? 1 : -1));

Upvotes: 0

Michael Coxon
Michael Coxon

Reputation: 5520

All you need to do is sum the votes by mapping true values to 1 and false values to -1.

_ItemsService.GetAll(x => x.ItemVotes)
    .Sum(v => v.Vote ? 1 : -1)

Upvotes: 1

Related Questions