I have a list that looks like this.
-100 Smith, Jane $1000
-100 Smith, John $1100.00
-100 Smith, Cole $840.00
-110 Jones, Harry $1270.00
-110 Jones, Diane $870.00
-111 Jones, Richard $1560.00
Using LINQ, I wish to write a query that will allow me to SUM up the values by family.
The first identifier is the family id.
For the above set of records, my output will look like:
-100 Smith $2940.00
-110 Jones $2140.00
-111 Jones $1560.00
Here's my code:
static void Main(string[] args)
{
_fi = FillIncome();
var query = (from f in _fi group f by f.familyId into delta select new {familyId = delta.Key, amount = delta.Sum(x => x.income) });
foreach (var q in query)
{
Console.WriteLine(q);
}}
static List<FamilyIncome> FillIncome()
{
var income = new List<FamilyIncome>();
var fi = new FamilyIncome {familyId = -100, lastname = "Smith", firstname = "Jane", income = 1000};
income.Add(fi);
fi = new FamilyIncome { familyId = -100, lastname = "Smith", firstname = "John", income = 1100 };
income.Add(fi);
fi = new FamilyIncome { familyId = -100, lastname = "Smith", firstname = "Cole", income = 840 };
income.Add(fi);
fi = new FamilyIncome { familyId = -110, lastname = "Jones", firstname = "Harry", income = 1270 };
income.Add(fi);
fi = new FamilyIncome { familyId = -110, lastname = "Jones", firstname = "Diane", income = 970 };
income.Add(fi);
fi = new FamilyIncome { familyId = -111, lastname = "Jones", firstname = "Richard", income = 1600 };
income.Add(fi);
return income;
}
Let's assume the class representing your data item looks like this:
class DataItem
{
public int FamilyId { get; set; }
public string FamilyName { get; set; }
public string FirstName { get; set; }
public decimal Salary { get; set; }
}
Then your data should be presented as
private static DataItem[] items = new[]
{
new DataItem { FamilyId = -100, FamilyName = "Smith", FirstName = "Jane", Salary = 1000},
new DataItem { FamilyId = -100, FamilyName = "Smith", FirstName = "John", Salary = 1100},
new DataItem { FamilyId = -100, FamilyName = "Smith", FirstName = "Cole", Salary = 840},
new DataItem { FamilyId = -110, FamilyName = "Jones", FirstName = "Harry", Salary = 1270},
new DataItem { FamilyId = -110, FamilyName = "Jones", FirstName = "Diane", Salary = 870},
new DataItem {FamilyId = -111, FamilyName = "Jones", FirstName = "Richard", Salary = 1560},
};
The query producing the desired output will be the following:
var groupedItems =
from item in items
group item by new {item.FamilyId, item.FamilyName}
into family
select new {family.Key.FamilyId, family.Key.FamilyName, Sum = family.Sum(item => item.Salary)};
And to output these grouped items you can use something like
foreach (var groupedItem in groupedItems)
{
Console.WriteLine(
"{0}\t{1}\t{2}",
groupedItem.FamilyId,
groupedItem.FamilyName,
groupedItem.Sum);
}
Hope this helps
Related
I have two collections as mentioned below. I have update two properties of "trade" from the other collection "refData" if those values exists in "refData".
Model:
class Trade
{
public int Id { get; set; }
public string PayIndex { get; set; }
public string RecIndex { get; set; }
public string PayCurrency { get; set; }
public string RecCurrency { get; set; }
}
class RefData
{
public string IndexLabel { get; set; }
public string Symbol { get; set; }
}
Sample Date:
var refData = new List<RefData>
{
new RefData { IndexLabel = "A1", Symbol="ABC1"},
new RefData { IndexLabel = "A2", Symbol="ABC2"},
new RefData { IndexLabel = "B1", Symbol="BCD1"},
new RefData { IndexLabel = "B2", Symbol="BCD2"},
};
var trades = new List<Trade>
{
new Trade { Id = 1, PayIndex = "A1", RecIndex = "B1"},
new Trade { Id = 2, PayIndex = "A2", RecIndex = ""},
new Trade { Id = 3, PayIndex = "", RecIndex = "B2"},
new Trade { Id = 4, PayIndex = "A3", RecIndex = "B3"}
};
I want to update PayCurrency and RecCurrency of "trades" with Symbol property of "refData" if trade's PayIndex and RecCurrency exists in "refData".
Output:
var Output = new List<Trade>
{
new Trade { Id = 1, PayIndex = "A1", RecIndex = "B1", PayCurrency = "ABC1", RecCurrency="BCD1"},
new Trade { Id = 2, PayIndex = "A2", RecIndex = "", PayCurrency = "ABC2", RecCurrency=""},
new Trade { Id = 3, PayIndex = "", RecIndex = "B2", PayCurrency = "", RecCurrency="BCD2"},
new Trade { Id = 4, PayIndex = "A3", RecIndex = "B3", PayCurrency = "", RecCurrency=""}
};
For c#6 and above you can do like below
var result = trades.Select(t => new Trade() {
Id= t.Id,
PayIndex = t.PayIndex,
RecIndex = t.RecIndex,
PayCurrency = refData.SingleOrDefault(r => r.IndexLabel.ToLower().Equals(t.PayIndex.ToLower()))?.Symbol ?? "",
RecCurrency = refData.SingleOrDefault(r => r.IndexLabel.ToLower().Equals(t.RecIndex.ToLower()))?.Symbol ?? ""
}).ToList();
For Older versions
var result = trades.Select(t => new Trade() {
Id= t.Id,
PayIndex = t.PayIndex,
RecIndex = t.RecIndex,
PayCurrency = refData.SingleOrDefault(r => r.IndexLabel.ToLower().Equals(t.PayIndex.ToLower())) != null ? refData.SingleOrDefault(r => r.IndexLabel.ToLower().Equals(t.PayIndex.ToLower())).Symbol : "",
RecCurrency = refData.SingleOrDefault(r => r.IndexLabel.ToLower().Equals(t.RecIndex.ToLower())) != null ? refData.SingleOrDefault(r => r.IndexLabel.ToLower().Equals(t.RecIndex.ToLower())).Symbol : ""
}).ToList();
Here is working example
Update using #SAJ answer
var output = (from r in trades
join p in refData on r.PayIndex equals p.IndexLabel
into g1
from s in g1.DefaultIfEmpty()
join t in refData on r.RecIndex equals t.IndexLabel into g2
from a in g2.DefaultIfEmpty()
select Trade { Id=r.Id,PayIndex=r.PayIndex,RecIndex=r.RecIndex, RecCurrency = a != null ? a.Symbol : "", PayCurrency = s != null ? s.Symbol : ""}).ToList();
You can try this
var output = (from r in trades
join p in refData on r.PayIndex equals p.IndexLabel
into g1
from s in g1.DefaultIfEmpty()
join t in refData on r.RecIndex equals t.IndexLabel into g2
from a in g2.DefaultIfEmpty()
select new { r, RecSymbol = a?.Symbol, PaySymbol = s?.Symbol }).ToList();
output.ForEach(o =>
{
o.r.PayCurrency = o.PaySymbol;
o.r.RecCurrency = o.RecSymbol;
});
I'm having problems with seeding some constant data when dropcreatedatabase is initiated. I have looked over some similar questions but I'm having difficulties understanding why my code isn't working.
All help is greatly appreciated (c:
Here is my SampleData Model with Seed Data
public class SampleData : DropCreateDatabaseIfModelChanges<fotmEntities>
{
protected override void Seed(fotmEntities context)
{
var genres = new List<Genre>
{
new Genre { Name = "Specials" },
new Genre { Name = "Online" },
new Genre { Name = "Services" },
new Genre { Name = "Food" },
new Genre { Name = "Misc" },
new Genre { Name = "Auto" }
};var localities = new List<Locality>
{
new Locality { Name = "Location1" },
new Locality { Name = "Location2" },
new Locality { Name = "Location3" },
new Locality { Name = "Location4" },
};
new List<Discount>
{
new Discount { Title = "Title A", Genre = genres.SingleOrDefault(g => g.Name == "Specials"), Information = "8.99M", Locality = localities.Single(a => a.Name == "Location1"), DiscountArtUrl = "/Content/img/placeholder.gif" },
new Discount { Title = "Title B", Genre = genres.SingleOrDefault(g => g.Name == "Specials"), Information = "8.99M", Locality = localities.Single(a => a.Name == "Location2"), DiscountArtUrl = "/Content/img/placeholder.gif" },
new Discount { Title = "Title C", Genre = genres.SingleOrDefault(g => g.Name == "Services"), Information = "8.99M", Locality = localities.Single(a => a.Name == "Location3"), DiscountArtUrl = "/Content/img/placeholder.gif" },
new Discount { Title = "Title D", Genre = genres.SingleOrDefault(g => g.Name == "Food"), Information = "8.99M", Locality = localities.Single(a => a.Name == "Location4"), DiscountArtUrl = "/Content/img/placeholder.gif" },
new Discount { Title = "Title E", Genre = genres.SingleOrDefault(g => g.Name == "Misc"), Information = "8.99M", Locality = localities.Single(a => a.Name == "Location1"), DiscountArtUrl = "/Content/img/placeholder.gif" },
new Discount { Title = "Title F", Genre = genres.SingleOrDefault(g => g.Name == "Auto"), Information = "8.99M", Locality = localities.Single(a => a.Name == "Location2"), DiscountArtUrl = "/Content/img/placeholder.gif" },
}.ForEach(a => context.Discounts.Add(a));
Here is my context model:
using System.Data.Entity;
public class fotmEntities : DbContext
{
public DbSet<Discount> Discounts { get; set; }
public DbSet<Genre> Genres { get; set; }
public DbSet<Locality> Localities { get; set; }
}
and my Global.asx
protected void Application_Start()
{
System.Data.Entity.Database.SetInitializer(
new fotm.Models.SampleData());
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
I am initializing the entities like this:
TasksListDB db = new TasksListDB();
public ActionResult Index()
{
var tasks1 = new List<Task>()
{
new Task { Id=1, Name = "Task 1", Difficulty = 1, DateCreated = DateTime.Parse("1/12/2011") , IsDone= true },
new Task { Id=2, Name = "Task 2", Difficulty = 2, DateCreated = DateTime.Parse("11/2/2011") , IsDone = false}
};
var tasks2 = new List<Task>()
{
new Task { Id=3, Name = "Task 3", Difficulty = 3, DateCreated = DateTime.Parse("11/2/2011") , IsDone = false},
new Task { Id=4, Name = "Task 4", Difficulty = 5, DateCreated = DateTime.Parse("1/2/2010") , IsDone= true }
};
tasks1.ForEach(t => db.Tasks.Add(t));
tasks2.ForEach(t => db.Tasks.Add(t));
var Persons = new List<Person> {
new Person { Id= 1 , Age= 10, EmailAddress= "asif_hameed_37#hotmail.com", FirstName= "Asif", LastName="Hameed" },
new Person { Id= 2 , Age= 10, EmailAddress= "asif_hameed_37#hotmail.com", FirstName= "Asif", LastName="Hameed" },
new Person { Id= 3 , Age= 10, EmailAddress= "asif_hameed_37#hotmail.com", FirstName= "Asif", LastName="Hameed" },
new Person { Id= 4 , Age= 10, EmailAddress= "asif_hameed_37#hotmail.com", FirstName= "Asif", LastName="Hameed" }};
Persons.ForEach(p => db.Persons.Add(p));
return View(db.Tasks);
}
my db context looks like this:
public class TasksListDB : DbContext
{
public DbSet<Person> Persons { get; set; }
public DbSet<Task> Tasks { get; set; }
}
I want to assign tasks to persons how can I do this ?
My model looks like this :
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public string EmailAddress { get; set; }
public virtual ICollection<Task> Tasks { get; set; }
}
public class Task
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public DateTime DateCreated { get; set; }
[Required]
[Range(1,5,ErrorMessage="Difficulty must be between 1 and 5")]
public int Difficulty { get; set; }
public bool IsDone { get; set; }
public virtual Person Person { get; set; }
}
When I run my mvc application, Person and Task tables are created but juntion table PersonTask not created.
[EDIT]
I am assigning tasks like this in initializer class:
var t1 = new Task { Id = 1, Name = "Urgent Task", DateCreated = DateTime.Now, Difficulty = 2, IsDone = true };
var t2 = new Task { Id = 2, Name = "Business Task", DateCreated = DateTime.Now, Difficulty = 1, IsDone = true };
var t3 = new Task { Id = 3, Name = "Home Task", DateCreated = DateTime.Now, Difficulty = 2, IsDone = false };
context.Tasks.Add(t1);
context.Tasks.Add(t2);
context.Tasks.Add(t3);
var Persons = new List<Person> {
new Person { Id= 1 , Age= 40, EmailAddress= "asif_hameed_37#hotmail.com", FirstName= "Asif", LastName="Hameed", Tasks= new List<Task> { t1,t2 }},
new Person { Id= 2 , Age= 30, EmailAddress= "asif_hameed_37#hotmail.com", FirstName= "Asif", LastName="Hameed" , Tasks= new List<Task> { t1} },
new Person { Id= 3 , Age= 29, EmailAddress= "asif_hameed_37#hotmail.com", FirstName= "Asif", LastName="Hameed" , Tasks= new List<Task> { t3,t2 } },
new Person { Id= 4 , Age= 35, EmailAddress= "asif_hameed_37#hotmail.com", FirstName= "Asif", LastName="Hameed" , Tasks= new List<Task> { t1,t3 } }};
context.Persons.Add(Persons[0]);
context.Persons.Add(Persons[1]);
context.Persons.Add(Persons[2]);
context.Persons.Add(Persons[3]);
context.SaveChanges();
but when i see the task table in sql server, it looks like this:
Id Name DateCreated Difficulty IsDone Person_Id
1 Urgent Task 2011-04-24 19:32:03.990 2 1 4
2 Business Task 2011-04-24 19:32:03.990 1 1 3
3 Home Task 2011-04-24 19:32:03.990 2 0 4
I assume that person table should have tasks not the task table should have person Id
Please suggest solution.
Junction table will not be created because you have defined one-to-many relation not many-to-many. The Task entity in your model can be assigned only to single Person not to many persons.
If you want to assing Task to Person simply run:
person.Tasks.Add(task);
I have a two objects as follows:
public class Item
{
public int ItemId {get;set;}
public string ItemName {get;set;}
public List<Tag> ItemTags {get;set;}
public DateTime DateCreated {get;set;}
}
public class Tag
{
public int TagId {get;set;}
public string TagName {get;set;}
}
These are LINQ-to-SQL objects, so the ItemTags will be an EntitySet.
I am trying to perform a search query where a user can provide a comma delimited list of tags as a search filter.
How do I filter my list of items to those which contains all of the tags in the comma delimited list.
EDIT2
e.g.
Item1 has tags of Apple, Banana, Orange
Item2 has tags of Banana, Orange
Item3 has tags of Pineapple, Orange
If the tag filter is "Banana, Orange" I need the results to be Item1 and Item2.
/EDIT2
This is what I have tried thus far:
string tags = "Manchester United,European Cup,2008";
List<string> tagsList = tags.Trim().ToLower()
.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Distinct(StringComparer.CurrentCultureIgnoreCase)
.ToList();
List<Item> itemList = ItemRepository.FetchAll();
var query = itemList
.OrderByDescending(p => p.DateCreated)
.ToList();
if (tagsList.Count() > 0)
{
query = query
.Where(p => p.ItemTags
.Select(q => q.TagName.ToLower())
.All(r => tagsList.Contains(r)))
.ToList();
}
However, this doesn't seem to work. Any ideas on what I am doing wrong please?
EDIT1: tags are trimmed and are 'lowercased'.
That because you're puting the tags from the items to lowercase, but not the searched tags.
With this modification it should work:
List<string> tagsList = tags
.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.ToLower())
.Distinct()
.ToList();
EDIT: OK, I see what the problem is: you're doing it backwards. You're searching for items that have only the tags that you're looking for.
Try that instead:
query =
(from item in query
let itemTags = p.ItemTags.Select(it => it.TagName.ToLower())
where tags.All(t => itemTags.Contains(t))
select item).ToList();
UPDATE: here's a version with the lambda syntax. It's pretty ugly because of the temporary anonymous type, but that's how the let clause translates to lambda...
query =
query.Select(item => new { item, itemTags = item.ItemTags.Select(it => it.TagName.ToLower()) })
.Where(x => tagsList.All(t => x.itemTags.Contains(t)))
.Select(x => x.item)
.ToList();
I think you need to do something like this:
var query = itemList.OrderByDescending(p => p.DateCreated).ToList();
var results = query.Where(i => i.ItemTags
.All(it => tagsList.Contains(it.TagName.ToLower())));
Then results should then be a list of matching items.
PS. Your code shows you fetching itemList as a List from your repository and then sorting by date created. This means the sorting isn't being done in the database. Once you turn something into a List you give up the benefits of deferred execution as you will bring back the entire collection into memory.
EDIT: Here's the test code to prove it works in Linq to Objects:
public class Item
{
public int ItemId { get; set; }
public string ItemName { get; set; }
public List<Tag> ItemTags { get; set; }
public DateTime DateCreated { get; set; }
}
public class Tag
{
public int TagId { get; set; }
public string TagName { get; set; }
}
class Program
{
static void Main(string[] args)
{
RunTags();
}
private static void RunTags()
{
Item i1 = new Item()
{
ItemId = 1,
ItemName = "Item1",
ItemTags = new List<Tag>() { new Tag { TagId = 1, TagName = "2008" }, new Tag { TagId = 2, TagName = "Donkey" } }
};
Item i2 = new Item()
{
ItemId = 2,
ItemName = "Item2",
ItemTags = new List<Tag>() { new Tag { TagId = 4, TagName = "Cat" }, new Tag { TagId = 2, TagName = "Donkey" }, new Tag { TagId = 3, TagName = "Seattle" } }
};
Item i3 = new Item()
{
ItemId = 3,
ItemName = "Item3",
ItemTags = new List<Tag>() { new Tag { TagId = 523, TagName = "Manchester united" }, new Tag { TagId = 10, TagName = "European Cup" }, new Tag { TagId = 1, TagName = "2008" } }
};
Item i4 = new Item()
{
ItemId = 4,
ItemName = "Item4",
ItemTags = new List<Tag>() { new Tag { TagId = 05, TagName = "Banana" }, new Tag { TagId = 140, TagName = "Foo" }, new Tag { TagId = 4, TagName = "Cat" } }
};
Item i5 = new Item()
{
ItemId = 5,
ItemName = "Item5",
ItemTags = new List<Tag>() { new Tag { TagId = 05, TagName = "Banana" }, new Tag { TagId = 140, TagName = "Foo" } }
};
List<Item> itemList = new List<Item>() { i1, i2, i3, i4, i5 };
string tags = "Manchester United,European Cup,2008";
List<string> tagsList = tags.Trim().ToLower()
.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Distinct(StringComparer.CurrentCultureIgnoreCase)
.ToList();
var query = itemList
.OrderByDescending(p => p.DateCreated).ToList();
var results = query.Where(i => i.ItemTags.All(it => tagsList.Contains(it.TagName.ToLower())));
foreach (var item in results)
{
Console.WriteLine(item.ItemName); // Should return "Item3"
}
Console.ReadLine();
}
If you want to match any of the tags in the Item's ItemTag list then just change All to Any i.e.
var results = query.Where(i => i.ItemTags.Any(it => tagsList.Contains(it.TagName.ToLower())));
I have an array of Person pocos, populated below. I'm trying display them alphabetically by Province, then by LastName within the Province. I'm using grouping and I can get the Provinces sorted fine, just not sure how to order the people within the province group.
This code:
Person[] people = new Person[]
{
new Person() { FirstName = "Tony", LastName = "Montana", Age = 39, HomeProvince = "Ontario" },
new Person() { FirstName = "Bill", LastName = "Smith", Age = 23, HomeProvince = "Ontario" },
new Person() { FirstName = "Jane", LastName = "Doe", Age = 23, HomeProvince = "Alberta" },
new Person() { FirstName = "John", LastName = "Doe", Age = 23, HomeProvince = "Alberta" },
new Person() { FirstName = "Alex", LastName = "DeLarge", Age = 19, HomeProvince = "British Columbia" },
new Person() { FirstName = "Travis", LastName = "Bickle", Age = 42, HomeProvince = "Quebec" },
new Person() { FirstName = "Ferris", LastName = "Beuller", Age = 17, HomeProvince = "Manitoba" },
new Person() { FirstName = "Maggie", LastName = "May", Age = 23, HomeProvince = "Ontario" },
new Person() { FirstName = "Mickey", LastName = "Mouse", Age = 93, HomeProvince = "Alberta" },
new Person() { FirstName = "Frank", LastName = "Darabont", Age = 49, HomeProvince = "Ontario" }
};
var query =
from person in people
group person by person.HomeProvince into g
orderby g.Key
select new { Province = g.Key, People = g };
foreach (var prov in query)
{
Console.WriteLine("{0}: ", prov.Province);
foreach (var person in prov.People)
{
Console.WriteLine(" {0} {1}, {2}", person.FirstName, person.LastName, person.Age);
}
}
Gives me this output:
Alberta:
Jane Doe, 23
John Doe, 23
Mickey Mouse, 93
British Columbia:
Alex DeLarge, 19
Manitoba:
Ferris Beuller, 17
Ontario:
Tony Montana, 39
Bill Smith, 23
Maggie May, 23
Frank Darabont, 49
Quebec:
Travis Bickle, 42
As you can see, the Provinces are listed alphabetically but how do I list the people within the province (i.e for Ontario I want this order: Darabont, Montana, May, Smith).
Assuming that you want alphabetical order by LastName then change this:
select new { Province = g.Key, People = g };
to:
select new { Province = g.Key, People = g.OrderBy(p => p.LastName) };
But note that your example "Darabont, Montana, May, Smith" is not quite in alphabetical order. I assume that this was just a mistake on your part, but if this is actually the order you want, please explain the rule you are using to generate this ordering.
should be able to, after the select statement, to orderby(person => person.LastName);