Distinct on Multiple Columns Entity Framework LINQ - c#

What is the LINQ Equivalent of
Select DISTINCT A, B, C from TESTDB WHERE ALPHA =1
I am trying something like this:
var data = TESTDB.WHERE(i=>i.ALPHA==1).SELECT(A,B,C).DISTINCT();

Using anonymous objects will do the trick:
var data = TESTDB.Where(i => i.ALPHA == 1).Select(i => new {i.A, i.B, i.C}).Distinct();
To retain the model:
List<Book> books = db.Book.Select(i => new Book {Author = i.Author, Title = i.Title}).Distinct().ToList();

You can also try
db.Table
.OrderBy(m=>m.Name)
.DistinctBy(m=> new{m.SerialNumber, m.Manufacturer})
.ToList();

If you use it like that:
var list = new List<Pet>()
{
new Cat() {Name = "Kitty", Id = 1},
new Cat() {Name = "Kitty", Id = 1},
new Cat() {Name = "Kitty", Id = 1}
};
var distinctCount = list.Where(i => i.Id == 1).Distinct().Count();
it turns out that distinctCount equals 3. Why is that? Seems that by default Distinct distinguishes between instances (even though all properties have the same values they're three instances).
You should implement custom comparer, here you'll find the code example: http://msdn.microsoft.com/en-us/library/bb338049.aspx.
Yet I'm not sure why do you want to select three properties (A,B,C). You can access single property in this way:
var data = list.Where(i => i.Id == 1).Distinct().SelectMany(i => i.Name);
However in order to select multiple properties you should cast the whole object to some class containing those properties:
var data = list.Where(i => i.Id == 1).Cast<Pet>().Distinct().ToList();

Related

How to use contain to filter multiple values

I have two table item table and transaction table. I need to use Contain to filter for two values.
var list1= table.select(c=> new {c.ID , c.ItemID}).tolist();
var list2 = tableItem.where(x=> list1.contains(x.id ,x.itemID ));
This should work for you, may require a few tweaks since you're syntax looks a little rough.
Note the Any LINQ function I used. More details here
var list1 = table.Select(c => new { c.ID, c.ItemID }).ToList();
var list2 = tableITem.Where(x => list1.Any(a => a.ID == x.id && a.ItemID == x.itemID)).ToList();

Get a specific value out of a DictionaryList with LINQ?

I have a Dictionary:
var dict = new Dictionary<int, string>();
With this values:
[0]: {[1, "Person1"]}
[1]: {[2, "Person2, Person3"]}
[2]: {[3, "Person4"]}
[3]: {[4, "Person5"]}
And when i use a "foreach" to get the values with "id" 2 i get as result "Person2, Person3".
foreach (var test in dict)
{
if (test.Key == 2)
System.Diagnostics.Debug.WriteLine(test.Value);
}
But when i use this LINQ line:
Person = dict.FirstOrDefault(q => q.Key == s.Person.ID).Value.ToString(),
I get this error:
Local sequence cannot be used in LINQ to SQL implementations of query
operators except the Contains operator.
I tried several things but nothing seems to work, so any ideas?
EDIT
I use this to show output on my page:
DataSelectionQuery = p => new
{
p.ID,
p.FirstName,
p.LastName,
TEST = dict.FirstOrDefault(q => q.Key == p.ID).Value.ToString(),
};
public Expression<Func<LinqClass, object>> DataSelectionQuery { get; set; }
And here is where it trows the error:
var results = query.Select(DataSelectionQuery).ToList();
You cannot use this kind of expressions inside Linq to SQL as they cannot be translated to SQL query
Use .ToList() at the end of your query, then use Linq to objects to complete your entity with values from Dictionary
For example when you have code like:
var result = from x in table
select new Entity
{
Id = x.Id,
Sth = x.Sth,
Person = dict.FirstOrDefault(q => q.Key == s.Person.ID).Value.ToString()
};
You need to change it to something like this:
var result = (from x in table
select new { x.Id, x.Sth, x.PersonId }) // LINQ To SQL part
.ToList() // get query results
.Select(x => new Entity
{
Id = x.Id,
Sth = x.Sth,
Person = dict.FirstOrDefault(q => q.Key == x.PersonId).Value.ToString()
}; // this part will be executed with Linq to objects
Please provide the full source code if you need more detailed help
Ok, so your "SelectionQuery" needs to be simple enough to translate into SQL query and should look like this:
DataSelectionQuery = p => new
{
p.ID,
p.FirstName,
p.LastName
};
And the other part should look like this:
var results = query.Select(DataSelectionQuery).ToList()
.Select(p => new
{
p.ID,
p.FirstName,
p.LastName,
TEST = dict.FirstOrDefault(q => q.Key == p.ID).Value.ToString()
});
Alernatively you can create your own class which will contain the Dictionary and will translate the ID into TEST on the getter action
This is how I solved the problem:
Person = string.Join(", ", PersonsQuery.Where(q => q.ID == s.ID).Select(q => q.PersonInformation))

How do i sum a list of items by code(or any field)?

I have an object that has a list of another object in it. i.e Object1 contains List<Object2>.
Assuming this is the definition of object 2:
public class Object2
{
string code,
string name,
decimal amount
}
I want to be a able to make a list2 from the list whose value will contain what something similar to what a select name, code, sum(amount) group by code kinda statement could have given me
this is what i did but it didnt contain what i needed on passing through.
var newlist = obj2List.GroupBy(x => x.code)
.Select(g => new { Amount = g.Sum(x => x.amount) });
I want code and name in the new list just like the sql statement above.
You're almost there:
var newlist = obj2List.GroupBy(x => x.code)
.Select(g => new
{
Code = g.First().code,
Name = g.First().name,
Amount = g.Sum(x => x.amount)
});
This groups the items by code and creates an anonymous object for each group, taking the code and name of first item of the group. (I assume that all items with the same code also have the same name.)
If you are grouping by code and not by name you'd have to choose something for name from the list, perhaps with First() or Last() or something.
var newlist = obj2List.GroupBy(x => x.code).Select(g => new {
Code = g.Key,
Name = g.First().name,
Amount = g.Sum(x => x.amount)
});
var query = Object1.Obj2List
.GroupBy(obj2 => obj2.code)
.Select(g => new {
Names = string.Join(",", g.Select(obj2.name)),
Code = g.Key,
Amount = g.Sum(obj2 => obj2.Amount)
});
Since you group by code only you need to aggregate the name also in some way. I have used string.Join to create a string like "Name1,Name2,Name3" for each code-group.
Now you could consume the query for example with a foreach:
foreach(var x in query)
{
Console.WriteLine("Code: {0} Names: {1} Amount: {2}"
, x.Code, x.Names, x.Amount);
}
Instead of using the LINQ Extension Methods .GroupBy() and .Select() you could also use a pure LINQ statement which is way easier to read if you come from a SQL Background.
var ls = new List<Object2>();
var newLs = from obj in ls
group obj by obj.code into codeGroup
select new { code = codeGroup.Key, amount = codeGroup.Sum(s => s.amount) };

How to use LINQ to select into an object?

I have data that looks like so:
UserId | SongId
-------- --------
1 1
1 4
1 12
2 95
I also have the following class:
class SongsForUser
{
public int User;
public List<int> Songs;
}
What I would like to do is use LINQ to select from my data to create a collection of SongsForUser objects. Below is what I have come up with so far:
var userCombos = songs.UserSongs.Select(x => new SongsForUser() { User = x.UserId,
Songs = /*What goes here?*/ });
How would I go about populating my Songs List?
So the result should be two SongsForUser objects. For user 1 it would have 3 items in the Songs list. For user 2 it would have 1 item in the Songs list.
songs.UserSongs.GroupBy(x => x.User).Select(g => new SongsForUser()
{
User = g.Key,
Songs = g.Select(s => s.SongId).ToList()
});
I suspect you want:
var songsByUser = songs.UserSongs
.GroupBy(song => song.UserId, song => song.SongId)
.Select(g => new SongsForUser { User = g.Key,
Songs = g.ToList() });
To explain, after the GroupBy you'll have a bunch of groups, where the key of each group is the user ID, and the values within the group are the song IDs:
Key = 1, Values = 1, 4, 12
Key = 2, Value = 95
Then you're just converting that into your SongsForUser type. Note that you don't need to explicitly include the () when calling the constructor in an object initializer - it's implicit unless you need to specify constructor arguments.
You could do this all in one GroupBy call, by the way:
var songsByUser = songs.UserSongs
.GroupBy(song => song.UserId, song => song.SongId,
(user, ids) => new SongsForUser { User = user,
Songs = ids.ToList() });
Personally I usually find a separate Select call to be more readable.
You can also do all of this with a query expression:
var songsByUser = from song in songs.UserSongs
group song.SongId by song.UserId into g
select new SongsForUser { User = g.Key, Songs = g.ToList() };
EDIT: The above is "provider-neutral" but it sounds like it's not working with LINQ to Entities. You may be able to get it to work like this:
var songsByUser = songs.UserSongs
.GroupBy(song => song.UserId, song => song.SongId)
.AsEnumerable()
.Select(g => new SongsForUser { User = g.Key,
Songs = g.ToList() });
The AsEnumerable call will force the grouping to be done in the database, but the final projection (including the ToList call) to be done locally. You should check the generated SQL for efficiency though.
Lets say you have the following:
public class SongsForUser
{
public int UserId;
public List<int> Songs;
}
Then a function like this one here will do. The list is just there to have
some data to test with.
public void Group()
{
List<Tuple<int, int>> SongRelations = new List<Tuple<int, int>>();
SongRelations.Add(new Tuple<int, int>(1, 1));
SongRelations.Add(new Tuple<int, int>(1, 4));
SongRelations.Add(new Tuple<int, int>(1, 12));
SongRelations.Add(new Tuple<int, int>(2, 95));
var list = SongRelations.GroupBy(s => s.Item1)
.Select(r => new SongsForUser()
{
UserId = r.Key,
Songs = r.Select(t => t.Item2).ToList(),
});
}
list contains 2 items of type SongsForUser afterwards.
One with user 1 and a list of songs containing 1, 4 and 12
and one with user 2 and a list of songs containing 95.
In its simplest form you can just:
List<MapPoint> points = db.PropertyResearches.Where(a => a.deptId == 66).Select(x => new MapPoint { property = x.notes.Substring(0, 10), latitude = x.lat, longitude = x.#long }).ToList();

How to make two Joins generate one select?

I currently have 3 tables:
News
ID | Title
Tag
ID | Name
TaggedContent
ContentID | TagID
And I have two context objects: NewsEntities and TagsEntities
I want to select all tags used by News, in my application I have:
static void Main(string[] args)
{
IEnumerable<dynamic> something = null;
IEnumerable<News.Data.News> news = null;
IEnumerable<Tags.Data.Tag> tags = null;
IEnumerable<TaggedContent> tagged = null;
using (var db = new NewsEntities())
{
news = db.News.ToList(); // 1 select
}
using (var db = new TagsEntities())
{
something = news.Join(db.TaggedContents.ToList(),
n => n.ID,
tc => tc.ContentID,
(n, tc) => new { tc.TagID }); // 1 select
something = something.Join(db.Tags.ToList(),
tid => tid.TagID,
t => t.ID,
(tid, t) => t); // 1 select
}
var result = something;
}
I am currently generating 3 selects. How can I reduce it to 2? Or if possible I would like to reduce to 1 without merging the entities.
Since you're joining entities from different contexts, you can't get away with fewer than 2 selects.
Your join is a simple identity check, so you could do this:
var ids = db.News.Select(x => x.ID).ToList();
You now have a local copy of all of the IDs in the news table -- the first select. Then change your first 'something' to:
something = db.TaggedContents
.Where(x => ids.Contains(x.ContentID))
.Select(x => new { x.TagID });
This second statement won't in itself generate a select because of deferred execution. You can now remove the ToList() in the third statement:
something = something.Join(db.Tags,
tid => tid.TagID,
t => t.ID,
(tid, t) => t);
And when you finally enumerate over something, you'll have your second select.
Using DataLoadOptions class passed in the creation of your DataContext.
DataLoadOptions options = new DataLoadOptions();
db.LoadOptions = options;
options.LoadWith((News n) => n.Tags);
So do for the remaining of the data to join.

Categories

Resources