This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
How can i made a pivot for this
I have a table as provided below
ID | value | Name
--------+---------------+------------------
1 Digital Model
1 companyA Manufacturer
2 Analog Model
2 CompanyB Manufacturer
3 Fiber Model
3 CompanyC Manufacturer
I need to convert this back to
ID | Model | Manufacturer
--------+--------------+-------------------
1 Digital companyA
2 Analog CompanyB
3 Fiber CompanyC
Please help me with the Linq Query for this. Using T-SQL, I am able to convert it properly. However, with Linq I am finding problems writing Query.
Try this:
var results = Data.GroupBy(l => l.Name);
.SelectMany( g =>
new
{
Metadata = g.Key,
data = g
});
var pivoted = new List<PivotedEntity>();
foreach(var item in results)
{
pivoted.Add(
new PivotedEntity
{
Id = item.Id,
Model = item.data.Where(x => x.Name == "Model")
.FirstOrDefault().value,
Manufacturer = item.data.Where(x => x.Name == "Manufacturer")
.FirstOrDefault().value,
});
}
You should first define a new class:
public class PivotedEntity
{
public int Id {get; set;}
public string Model {get; set;}
public string Manufacturer {get; set;}
}
Related
I have my model as:
// Person
public class Person {
public int Id {get; set}
public string FirstName {get; set}
public string LastName {get; set}
public ICollection<PersonCourse> PersonCourses {get; set;}
}
// PersonCourse
public class PersonCourse {
public int PersonId {get; set}
public Person Person {get; set;}
public int CourseId {get; set}
public Course Course {get; set}
}
// Course
public class Course {
public int Id {get; set}
public string Name {get; set}
public ICollection<PersonCourse> PersonCourses {get; set;}
}
I have the following data:
// Person
ID FIRSTNAME LASTNAME
1 John Doe
2 Jane Doe
// PersonCourse
PersonId CourseId
1 1
1 2
2 1
2 3
// Course
ID NAME
1 Course1
2 Course2
3 Course3
How can I write a Lambda Method query to get the data as an IEnumerable as:
John Doe Course1
John Doe Course2
Jane Doe Course1
Jane Doe Course3
Currently, I have the courses listed in a CSV format like below:
// NOTE:
// MUST start query from DBSet<Person> not from DBSet<PersonCourse>!
var data = db.Persons.Select(x => new {
FirstName = x.FirstName,
LastName = x.LastName,
CourseName = string.Join(",", x.PersonCourses.Select(c => c.Course.Name)
});
The best approach for this is:
var data = db.Persons.SelectMany(c => c.PersonCourses.Select(x => c.FirstName + " " + c.LastName + " " + x.Course.Name));
Assuming that everything is not null. I prefer always to manage Null safety, but that depends on the final result that you want (discard if is a single null or fill with empty string)
To get multiple rows per Person you need a Join or a SelectMany.
Using the navigation properties you can do this:
var data = db.Persons.SelectMany
(
p => p.PersonCourses,
(p, pc) => new { p.FirstName, p.LastName, pc.Course.Name }
);
Or you can do the uglier but more general join, avoiding the navigation properties:
var data = db.Persons
.Join
(
db.PersonCourses, p => p.Id, pc => pc.PersonId,
(p, pc) => new { p, pc }
)
.Join
(
db.Courses, j => j.pc.CourseId, c => c.Id,
(j, c) => new { j.p.FirstName, jn.p.LastName, c.Name }
);
Joins work when the navigation properties aren't available (POCOs in memory for instance) and aren't all that hard to work with... except for that pesky intermediate j but you get used to it.
I have a category class as shown.
/// <summary>
/// A category / matrix item
/// </summary>
public class Category : BaseItem
{
public Category()
{
}
[Key]
public int CategoryId { get; set; }
[MaxLength(256, ErrorMessage = "Text cannot be longer than 256 characters")]
[Required(ErrorMessage = "Item text is required")]
public string Text { get; set; }
public int? ParentCategoryId { get; set; }
public virtual Category Parent { get; set; }
}
I am attempting to write a function that allows me to get only the elements who have no children. I am struggling to wrap my head around the recursion logic. I am not opposed to using a loop and just building a list, however I am hoping to be able to do something like joining the data back to itself, DefaultIfEmpty()
then join again where joined record is null.
IE:
ID| Text | ParentId
1 | Parent | null
2 | Child | 1
3 | asdf | 2
4 | asdf | 1
I would only want to retrieve records 3 and 4 as they have no children.
I have referenced this post for the full tree functions, but need a bit more to get only the childless elements.
Thanks to the comments on this question, i was able to accomplish this using the query below.
public IEnumerable<Category> GetChildlessCategories()
{
return WowContext.Categories
.GroupJoin(
WowContext.Categories.Where(category => category.ParentCategoryId.HasValue),
(category) => category.CategoryId,
(child) => child.ParentCategoryId.Value,
(category, children) => new { Children = children, Category = category })
.Where(a => !a.Children.Any())
.Select(a => a.Category).ToList();
}
Hi all I am using LINQ to query a table like this:
AlbumID | Spotify | iTunes
--------------------------
5 | 12 | 4
5 | 18 | 6
6 | 10 | 8
I would like to add together rows with the same unique AlbumID, I am aware of GroupBy but that groups together all similar IDs so I used Where that uses albumID (an input arg in this example it could be 5)
var platformArray = Context.Analytics.Where(x => x.AlbumID == albumId).Select(s => new
{
Id = s.AlbumID,
Spotify = s.SpotifyClicks,
Itunes = s.ItunesClicks
});
But when I print out to debug console, it does not combine the unique ID's
foreach (var item in platformArray)
{
Debug.WriteLine(item.Id);
Debug.WriteLine(item.Spotify);
Debug.WriteLine(item.Itunes);
}
I see (added arrows for clarity):
5 <-ID
12 <-Spotify
4 <-ITunes
5 <-ID
18 <-Spotify
6 <-ITunes
How can I reformat my LINQ code to print out something like:
5 <-ID
30 <-Spotify
10 <-Itunes
Thanks!
EDIT: adding my Analytic Class
public class Analytic
{
private object p1;
private object p2;
private object p3;
public Analytic(object p1, object p2, object p3)
{
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
}
[Key]
public int Id { get; set; }
public int AlbumID { get; set; }
public int SpotifyClicks { get; set; }
public int ItunesClicks { get; set; }
public virtual Album Album { get; set; }
}
Try below code:
var List = from element in prod
group element by new { element.Id } into g
select new yourClass
(
Id = g.First().ID,
Spotify = g.Sum(s => s.Spotify)
Itunes = g.Sum(s => s.Itunes)
);
So I managed to figure this out from the help of Hasan's comment on my original post and some additional fixes:
var query = Context.Analytics
.Where(x => x.AlbumID == albumId)
.GroupBy(c => c.AlbumID)
.Select(
g =>
new
{
Id = g.Key,
Spotify = g.Sum(s => s.SpotifyClicks),
ITunes = g.Sum(s => s.ItunesClicks),
}
);
This only queries for my specific albumID and sums together all rows with that specific ID!
Thanks everyone
So after you have grouped your Albums into groups of Albums with the same AlbumId, you want to transform all Albums in the group into one object, containing the common AlbumId, the total of all Spotify of all Albums in your group, and the total of all iTunes of all Albums in your group.
var result = albums.GroupBy(album => album.AlbumId)
// from every group, make one element with the sum of Spotify and sum of itunes:
.Select(group => new
{
AlbumId = group.Key,
Spotify = group.Select(groupElement => groupElement.Spotify).Sum(),
ITunes = group.Select(groupElement => groupElement.Itunes).Sum(),
});
Simple comme bonjour!
Context Class
Here is my context class. It sort of works in that in the BlogsPostTags table it adds IDs of the post and IDs of the tags ... however it adds NEW tag IDs, not exsisting tags that are already stored in the database.
public class WebsiteDBContext : DbContext
{
public DbSet<BlogPost> BlogPosts { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<Author> Authors { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BlogPost>().HasRequired(bp => bp.Author).WithMany(a => a.BlogPosts);
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<BlogPost>().HasMany(bp => bp.Tags).WithMany(t => t.Posts).Map(m =>
{
m.MapLeftKey("BlogPostID");
m.MapRightKey("TagID");
m.ToTable("BlogPostTags");
});
}
}
What I want to happen:
Tag Table BlogPostTags Table
ID Name BlogID TagID
---------------- ----------------
1 Dogs 1 1
2 Cats 1 9
3 Birds 1 11
4 Horses
5 Rabbits
6 Reptiles
7 Insects
8 Nature
9 Puppies
10 Kittens
11 Cute
12 Products
But what really happens is that it creates NEW tags with IDs into the Tags Table. Like so
Tag Table BlogPostTags Table
ID Name BlogID TagID
---------------- ----------------
1 Dogs 1 13
2 Cats 1 14
3 Birds 1 15
4 Horses
5 Rabbits
6 Reptiles
7 Insects
8 Nature
9 Puppies
10 Kittens
11 Cute
12 Products
13 Dogs
14 Puppies
15 Cute
Ovbviously duplicate Names are no good! how can I fix this so that I get the expected result? Heres my ActionResult Method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Title,PublishDate,PublishTime,Body,AuthorID,Tags")] CreateBlogPostVM blogpost)
{
List<Tag> tags = new List<Tag>();
foreach (var tag in blogpost.Tags.Where(t => t.IsChecked == true))
{
tags.Add(new Tag { TagID = tag.TagID, Name = tag.Name });
}
if (ModelState.IsValid)
{
BlogPost newPost = new BlogPost
{
Title = blogpost.Title,
PublishDate = blogpost.PublishDate,
PublishTime = blogpost.PublishTime,
Body = blogpost.Body,
Author = db.Authors.Where(bp => bp.AuthorID == blogpost.AuthorID).Single(),
Tags = tags //Add list of tags
};
db.BlogPosts.Add(newPost);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(blogpost);
}
You need to find the EXISTING tags that already exist in your db rather than adding new ones. Replace this:
foreach (var tag in blogpost.Tags.Where(t => t.IsChecked == true))
{
tags.Add(new Tag { TagID = tag.TagID, Name = tag.Name });
}
with:
foreach (var tag in blogpost.Tags.Where(t => t.IsChecked == true))
{
tags.Add(db.Tags.Find(tag.TagID));
}
(this assumes TagID is the primary key for your tag table, if not you can do something like tags.Add(db.Tags.Where(t=>t.TagID == tag.TagID).First()))
I'd wish to create a list of the following class (List):
public class StatusData
{
public DateTime? Date { get; set; }
public int RegisteredUsers { get; set; }
public int Orders { get; set; }
public decimal Import { get; set; }
}
This list should be filled with with information of another 2 lists: a List<Contact> and a List<Purchase>. Both classes have the following shape:
public class Contact
{
public DateTime? RegisterDate { get; set; }
public string Name { get; set; }
}
public class Purchase
{
public DateTime? PurchaseDate { get; set; }
public int PurchaseID {get;set;}
public int Import { get; set; }
}
The idea is that the List should, for every day, which is the amount of Registered users, the amount of purchases and the total import of these purchases. So, in essence, both just share the Date.
However, I can't come with a good LINQ expression to create the List. One solution I considered is to retrieve all the distinct dates among the 2 lists and then iterate over them to retrieve the information I want, but I think this approach is rather "ugly". Is there any way to do what I want to do easily?
EDIT: An example of what I want would be the following:
Contacts
--------
Name RegisteredDate
David 10/10/2013
Paul 09/10/2013
Gina 10/10/2013
Roger 09/10/2013
Rose 05/10/2013
Jean 07/10/2013
Mark 04/10/2013
Lisa 04/10/2013
Purchases
-----------
ID PurchaseDate Import
1 10/10/2013 10
2 10/10/2013 10
3 10/10/2013 20
4 04/10/2013 15
5 04/10/2013 15
6 07/10/2013 20
7 07/10/2013 2
8 07/10/2013 2
Expected result
----------------
Date RegisteredUsers Purchases Import
04/10/2013 2 2 30
05/10/2013 1 0 0
07/10/2013 1 3 24
09/10/2013 2 0 0
10/10/2013 2 3 40
Kind regards
var contacts = new List<Contact>();
var purchases = new List<Purchase>();
var dates = contacts.Select(x => x.RegisterDate.Value)
.Concat(purchases.Select(x => x.PurchaseDate.Value))
.Distinct();
var data = from date in dates
join c in contacts on date equals c.RegisterDate.Value into registered
join p in purchases on date equals p.PurchaseDate.Value into purchased
select new StatusData {
Date = date,
RegisteredUsers = registered.Count(),
Orders = purchases.Count(),
Import = purchases.Sum(x => x.Import)
};
It will return StatusData for all days, when at least one registration OR one purchase was made.
So there are several separate tasks here. The first thing that you're doing is grouping your contacts by the registration date. Next, you're joining that result to the Purchases table based on the date, aggregating count/sums on two different columns. LINQ has a method for each of these two operations:
var query = from contact in contacts
group contact by contact.RegisterDate into contactGroup
join purchase in purchases
on contactGroup.Key equals purchase.PurchaseDate into purchaseGroup
select new StatusData
{
Date = contactGroup.Key,
RegisteredUsers = contactGroup.Count(),
Orders = purchaseGroup.Count(),
Import = purchaseGroup.Sum(p => p.Import),
};
This is really two separate queries with their results merged at the end.
var contact = from c in Contact
group c by c.RegisterDate into c
select new StatusData
{
Date = c.Key,
RegisteredUsers = c.Count(),
Orders = 0,
Import = 0,
};
var purchases = from p in Purchases
group p by p.PurchaseDate into p
select new StatusData
{
Date = p.Key,
RegisteredUsers = 0,
Orders = p.Count(),
Import = p.Sum(x => x.Import),
};
var final = from x in contact.Concat(purchases)
group x by x.Date into x
select new StatusData
{
Date = x.Key,
RegisteredUsers = x.Sum(y => y.RegisteredUsers),
Orders = x.Sum(y => y.Orders),
Import = x.Sum(y => y.Import),
};