Related
A sequence of data about applicants nameList of type Entrant is given. Each element of the sequence includes the fields School number, Year of entering, Last name. Get data (list of YearSchoolStat values) about the number of different schools that applicants graduated from for each year present in the source data. The YearSchoolStat type includes the Year of entering, Number of Schools fields. The list of YearSchoolStat values must be sorted in ascending order of the number of schools, and for matching values, in ascending order of the year number. Example of data provided and expected results:
nameList: new[]
{
new Entrant {LastName = "Name", SchoolNumber = 12, Year = 2019},
new Entrant {LastName = "Name", SchoolNumber = 12, Year = 2019},
new Entrant {LastName = "Name", SchoolNumber = 13, Year = 2019},
new Entrant {LastName = "Name", SchoolNumber = 14, Year = 2019},
new Entrant {LastName = "Name", SchoolNumber = 15, Year = 2019},
new Entrant {LastName = "Name", SchoolNumber = 12, Year = 2018},
new Entrant {LastName = "Name", SchoolNumber = 12, Year = 2018},
new Entrant {LastName = "Name", SchoolNumber = 13, Year = 2018},
new Entrant {LastName = "Name", SchoolNumber = 12, Year = 2017},
new Entrant {LastName = "Name", SchoolNumber = 12, Year = 2017}
},
expected: new[]
{
new YearSchoolStat {NumberOfSchools = 1, Year = 2017},
new YearSchoolStat {NumberOfSchools = 2, Year = 2018},
new YearSchoolStat {NumberOfSchools = 4, Year = 2019}
});
I'm trying to group by SchoolNumber and Year and then for the number of schools I want to use something like Count() but it's not permitted.
var result = nameList.GroupBy(c => new
{
c.SchoolNumber,
c.Year,
}).Select(ss => new YearSchoolStat()
{
Year = ss.Key.Year,
NumberOfSchools = ss.Key.SchoolNumber
});
What is wrong with my approach and what else I should try?
{NumberOfSchools = 1, Year = 2017},
{NumberOfSchools = 2, Year = 2018},
{NumberOfSchools = 4, Year = 2019}
the following code will give the above output
var result = nameList.GroupBy(c => new
{
c.Year, c.SchoolNumber
}).GroupBy(c => new
{
c.Key.Year
}).Select(ss => new YearSchoolStat()
{
Year = ss.Key.Year,
NumberOfSchools = ss.Count()
});
If we want to group by year, let us group it by year only. That 'll help us to count the result subsets:
var result = nameList
.GroupBy(c => c.Year)
.Select(ss => new YearSchoolStat()
{
Year = ss.Key,
NumberOfSchools = ss.Count() // Here is the magic
});
I have a list of Receipts
IEnumerable<Receipt> Receipts =
new List<Receipt>()
{
new Receipt
{
Id = 1, CustomerId = 1, IsCheckedOut = false, OperationDate = new DateTime(2021, 1, 2),
ReceiptDetails = new List<ReceiptDetail>()
{
new ReceiptDetail { Id = 1, ProductId = 1, UnitPrice = 10, Product = ProductEntities.ElementAt(0), DiscountUnitPrice = 9, Quantity = 2, ReceiptId = 1 },
new ReceiptDetail { Id = 2, ProductId = 2, UnitPrice = 20, Product = ProductEntities.ElementAt(1), DiscountUnitPrice = 19, Quantity = 8, ReceiptId = 1},
new ReceiptDetail { Id = 3, ProductId = 3, UnitPrice = 25, Product = ProductEntities.ElementAt(2), DiscountUnitPrice = 24, Quantity = 1, ReceiptId = 1 },
}
},
new Receipt
{
Id = 2, CustomerId = 2, IsCheckedOut = false, OperationDate = new DateTime(2021, 1, 15),
ReceiptDetails = new List<ReceiptDetail>()
{
new ReceiptDetail { Id = 4, ProductId = 1, UnitPrice = 10, Product = ProductEntities.ElementAt(0), DiscountUnitPrice = 9, Quantity = 10, ReceiptId = 2 },
new ReceiptDetail { Id = 5, ProductId = 3, UnitPrice = 25, Product = ProductEntities.ElementAt(2), DiscountUnitPrice = 24, Quantity = 1, ReceiptId = 2 }
}
},
new Receipt
{
Id = 3, CustomerId = 1, IsCheckedOut = false, OperationDate = new DateTime(2021, 2, 15),
ReceiptDetails = new List<ReceiptDetail>()
{
new ReceiptDetail { Id = 6, ProductId = 1, UnitPrice = 10, Product = ProductEntities.ElementAt(0), DiscountUnitPrice = 9, Quantity = 10, ReceiptId = 3 },
new ReceiptDetail { Id = 7, ProductId = 2, UnitPrice = 25, Product = ProductEntities.ElementAt(1), DiscountUnitPrice = 24, Quantity = 1, ReceiptId = 3 }
}
}
};
I need to get the best selling(by quantity) products based on CustomerId
Here is my function
public IEnumerable<Product> GetMostSoldProductByCustomer(int customerId, int productCount)
{
var a = ReceiptEntities.Where(rd => rd.CustomerId == customerId)..SelectMany(x => x.ReceiptDetails);
var b = a.GroupBy(rd => rd.ProductId);
var c = b.Select(p => p.Select(y => y.Quantity).Sum());
}
I'm stuck on this, I have no idea how to connect b and c.
For better understanding, if customerId = 1 and productCount = 3. The function should return 3 products with Id = 1, 2, 3 accordingly in descending order by quantities
One note! The customer whose id = 1 has two receipts, that's why I'm calculating the sum of Quantity as there are the same products in different receipts
Try the following query:
public IEnumerable<int> GetMostSoldProductByCustomer(int customerId, int productCount)
{
var query =
from re in ReceiptEntities
where re.CustomerId == customerId
from rd in re.ReceiptDetails
group rd by rd.ProductId into g
select new
{
ProductId = g.Key,
TotalQuantity = g.Sum(x => x.Quantity)
} into s
orderby descending s.TotalQuantity
select s.ProductId;
return query;
}
I have these lists:
var subjects = new List<SubjectModel>
{
new SubjectModel { subjId = 1, subjName = "Math" },
new SubjectModel { subjId = 2, subjName = "Science" },
new SubjectModel { subjId = 3, subjName = "History" },
new SubjectModel { subjId = 4, subjName = "Language" }
};
var quizzes = new List<QuizModel>
{
new QuizModel { quizId = 1, quizDate = DateTime.Parse("2016-11-25"), quizScore = 10, subjectId = 1 },
new QuizModel { quizId = 2, quizDate = DateTime.Parse("2016-11-25"), quizScore = 15, subjectId = 1 },
new QuizModel { quizId = 3, quizDate = DateTime.Parse("2016-11-25"), quizScore = 8, subjectId = 2 },
new QuizModel { quizId = 4, quizDate = DateTime.Parse("2016-11-26"), quizScore = 13, subjectId = 1 },
new QuizModel { quizId = 5, quizDate = DateTime.Parse("2016-11-26"), quizScore = 20, subjectId = 2 }
};
var exams = new List<ExamModel>
{
new ExamModel { examId = 1, examDate = DateTime.Parse("2016-11-25"), examScore = 90, subjectId = 1 },
new ExamModel { examId = 2, examDate = DateTime.Parse("2016-11-25"), examScore = 88, subjectId = 2 },
new ExamModel { examId = 3, examDate = DateTime.Parse("2016-11-25"), examScore = 92, subjectId = 4 },
new ExamModel { examId = , examDate = DateTime.Parse("2016-11-26"), examScore = 84, subjectId = 1 },
};
var exercises = new List<ExerciseModel>
{
new ExerciseModel { exerciseId = 1, exerciseDate = DateTime.Parse("2016-11-25"), exerciseScore = 17, subjectId = 1 },
new ExerciseModel { exerciseId = 2, exerciseDate = DateTime.Parse("2016-11-25"), exerciseScore = 15, subjectId = 2 },
new ExerciseModel { exerciseId = 3, exerciseDate = DateTime.Parse("2016-11-26"), exerciseScore = 15, subjectId = 1 },
new ExerciseModel { exerciseId = 4, exerciseDate = DateTime.Parse("2016-11-26"), exerciseScore = 12, subjectId = 4 },
new ExerciseModel { exerciseId = 5, exerciseDate = DateTime.Parse("2016-11-26"), exerciseScore = 10, subjectId = 1 },
};
I was able to successfully group each of them by date and by subject.
var allQuizzes = quizzes.GroupBy(qz => qz.quizDate, (q, values) =>
new
{
Date = q,
Quizzes = values.GroupBy(v => v.subjectId, (c, values2) =>
new {
SubjectId = c,
QuizSum = values2.Sum(v2 => v2.quizScore)
})
});
var allExercises = exercises.GroupBy(ex => ex.exerciseDate, (e, values) =>
new {
Date = e,
Exercises = values.GroupBy(x => x.subjectId, (z, values2) =>
new {
SubjectId = z,
ExerSum = values2.Sum(r => r.exerciseScore)
})
});
var allExams = exams.GroupBy(ex => ex.examDate, (e, values) =>
new
{
Date = e,
Exercises = values.GroupBy(x => x.subjectId, (z, values2) =>
new
{
SubjectId = z,
ExamSum = values2.Sum(r => r.examScore)
})
});
However, I need to join all three of them to get the sum of all scores. The final table should display like this.
-----------------------------------------------------------------
| Date | Math | Science | History | Language |
| 11/25/2016 | 132 | 111 | 0 | 92 |
| 11/26/2016 | 122 | 20 | 0 | 12 |
-----------------------------------------------------------------
I tried to join them, but it can't seem to join by multiple columns.
I select from all 3 collections results in form of the same anonymous class (the same Idea had Andrei in first answer), that allows me just to collect all results together in all list, without mapping and converting.
var allQuiz = quizzes.GroupBy(x => new { x.subjectId, x.quizDate })
.Select(x => new {
Date = x.Key.quizDate,
Subj = x.Key.subjectId,
Sum = x.Sum(r=>r.quizScore)});
var allExam= exams.GroupBy(x => new { x.subjectId, x.examDate })
.Select(x => new {
Date = x.Key.examDate,
Subj = x.Key.subjectId,
Sum = x.Sum(r=>r.examScore)});
var allExc = exercises.GroupBy(x => new { x.subjectId, x.exerciseDate })
.Select(x => new {
Date = x.Key.exerciseDate,
Subj = x.Key.subjectId,
Sum = x.Sum(r=>r.exerciseScore)});
Combining of all results together:
var all = allQuiz.ToList();
all.AddRange(allExam.ToList());
all.AddRange(allExc.ToList());
var result = all.GroupBy(x => new { x.Date, x.Subj })
.Select(x => new { x.Key.Date, x.Key.Subj, Sum = x.Sum(s => s.Sum)});
var list = result.GroupBy(r => r.Date).Select(x => new {
Date = x.Key,
Math = x.SingleOrDefault(t=>t.Subj==1)?.Sum ?? 0,
Science = x.SingleOrDefault(t=>t.Subj==2)?.Sum ?? 0,
History = x.SingleOrDefault(t=>t.Subj==3)?.Sum ?? 0,
Language = x.SingleOrDefault(t=>t.Subj==4)?.Sum ?? 0,
});
Output in LinqPad:
Here is an idea. Instead of keeping the distinction while grouping, you could convert all three to the same structure. For instance:
var allQuizzes = quizzes.GroupBy(qz => qz.quizDate, (q, values) =>
new
{
Date = q,
Results = values.GroupBy(v => v.subjectId, (c, values2) =>
new {
SubjectId = c,
Sum = values2.Sum(v2 => v2.quizScore)
})
});
Notice names "Results" and "Sum" - you can use the same for the other two objects. And now you have three collections, all of the same structure:
{
Date:
Results: [
{SubjectId, Sum}
{SubjectId, Sum}
...
]
}
Since they are all the same now, you can stop treating them differently, use UNION to merge all three, group them by date and within that by subject. Then you could probably iterate through subject list to get necessary info, depends on what you mean by "final table".
This is what i came up with.
It may not be best optimized, but might be enough for you.
I rendered the results into a StringBuilder in my test.
var result =
quizzes.Select(q => new {SubjectId = q.subjectId, Date = q.quizDate, Score = q.quizScore})
.Union(exams.Select(e => new {SubjectId = e.subjectId, Date = e.examDate, Score = e.examScore}))
.Union(exercises.Select(e => new {SubjectId = e.subjectId, Date = e.exerciseDate, Score = e.exerciseScore}))
.GroupBy(arg => arg.Date,
(key, values)=>
new
{
Key = key,
Scores = values.GroupBy(v => v.SubjectId, (s, values2) => new { SubjectId = s, SumScore = values2.Sum(v2 => v2.Score) })
});
StringBuilder sb = new StringBuilder("Date\t\t");
foreach (SubjectModel subject in subjects)
{
sb.Append($"{subject.subjName}\t");
}
sb.AppendLine();
foreach (var record in result)
{
sb.Append($"{record.Key.ToShortDateString()}\t");
foreach (SubjectModel subject in subjects)
{
int sum = record.Scores.Where(s => s.SubjectId == subject.subjId).Select(s => s.SumScore).DefaultIfEmpty(0).Single();
sb.Append($"{sum}\t");
}
sb.AppendLine();
}
string finalTable = sb.ToString();
Instead of using three different anonymous objects to hold the results, make your own class:
public enum TestType
{
Quiz,
Exam,
Exercise,
}
public class TestScore
{
public TestType Type { get; set; }
public DateTime Date { get; set; }
public int Score { get; set; }
public int SubjectId { get; set; }
// Constructors - make a TestScore object
public TestScore(QuizModel q)
{
Type = TestType.Quiz;
Date = q.quizDate;
Score = q.quizScore;
SubjectId = q.SubjectId;
}
public TestScore(ExamModel e)
{
Type = TestType.Exam;
Date = e.examDate;
Score = e.examScore;
SubjectId = e.SubjectId;
}
public TestScore(ExerciseModel e)
{
Type = TestType.Exercise;
Date = e.exerciseDate;
Score = e.exerciseScore;
SubjectId = e.SubjectId;
}
}
Convert to TestScore:
List<TestScore> scores = new List<TestScore>();
scores.AddRange(quizzes.Select(q => new TestScore(q));
scores.AddRange(exams.Select(e => new TestScore(e));
scores.AddRange(exercises.Select(e => new TestScore(e));
Now you have one datasource instead of three, displaying the results becomes easy.
Question : I have a list of items which I need to first group and then sub group by the number of items in their product group. Code below. The objective is to create matching groups where the zones and the products available in each match. The zone and product are subject to change, but the number products available should always be grouped.
for example. given the below...
Result should be...
Group 1
Zone = "EAST", Product = "Bananas", ShippingTime = "3_Days" };
Zone = "EAST", Product = "Oranges", ShippingTime = "5_Days" };
Zone = "SOUTH", Product = "Bananas", ShippingTime = "3_Days" };
Zone = "SOUTH", Product = "Oranges", ShippingTime = "10_Days" };
Group 2
Zone = "WEST", Product = "Oranges", ShippingTime = "3_Days" };
Zone = "WEST", Product = "Oranges", ShippingTime = "5_Days" };
Zone = "WEST", Product = "Oranges", ShippingTime = "10_Days" };
Zone = "WEST", Product = "Apples", ShippingTime = "3_Days" };
Zone = "WEST", Product = "Apples", ShippingTime = "5_Days" };
Zone = "WEST", Product = "Apples", ShippingTime = "5_Days" };
Zone = "WEST", Product = "Bananas", ShippingTime = "3_Days" };
Zone = "WEST", Product = "Bananas", ShippingTime = "5_Days" };
Zone = "WEST", Product = "Bananas", ShippingTime = "5_Days" };
The ideal grouping would be to break out the below from Group 2 (caus' they match what's in group 1) and add to
group 1, thus leaving the residual in group 2.
Zone = "WEST", Product = "Bananas", ShippingTime = "3_Days" };
Zone = "WEST", Product = "Oranges", ShippingTime = "10_Days" };
Here's the test I've been working with. Nothing I've been able to do with linq seems to get the job done.
Thanks in Advance for the ideas.
public void should_group_products_and_shippingtimes()
{
{
Bananas = "Limited DR";
var a = new MyClass { Id = 1, Zone = "EAST", Product = "Bananas", ShippingTime = "3_Days" };
var b = new MyClass { Id = 2, Zone = "EAST", Product = "Oranges", ShippingTime = "5_Days" };
var c = new MyClass { Id = 3, Zone = "SOUTH", Product = "Bananas", ShippingTime = "3_Days" };
var d = new MyClass { Id = 4, Zone = "SOUTH", Product = "Oranges", ShippingTime = "10_Days" };
var e = new MyClass { Id = 5, Zone = "WEST", Product = "Oranges", ShippingTime = "3_Days" };
var f = new MyClass { Id = 6, Zone = "WEST", Product = "Oranges", ShippingTime = "5_Days" };
var g = new MyClass { Id = 7, Zone = "WEST", Product = "Oranges", ShippingTime = "10_Days" };
var h = new MyClass { Id = 8, Zone = "WEST", Product = "Apples", ShippingTime = "3_Days" };
var i = new MyClass { Id = 9, Zone = "WEST", Product = "Apples", ShippingTime = "5_Days" };
var j = new MyClass { Id = 10, Zone = "WEST", Product = "Apples", ShippingTime = "5_Days" };
var k = new MyClass { Id = 11, Zone = "WEST", Product = "Bananas", ShippingTime = "3_Days" };
var l = new MyClass { Id = 12, Zone = "WEST", Product = "Bananas", ShippingTime = "5_Days" };
var m = new MyClass { Id = 13, Zone = "WEST", Product = "Bananas", ShippingTime = "5_Days" };
var myList = new List<MyClass>();
myList.AddRange(new[] {a,b,c,d,e,f,g,h,i,j,k,l,m});
var sublist = (from ee in myList
from ff in myList
where ee.Product == ff.Product
&& ee.Id != ff.Id
select ee).Distinct();
var match1 =
myList.AsEnumerable().GroupBy(
record =>
new { PRODUCT = record.Product, SHIPPINGTIME = record.ShippingTime }).Where(z => z.Count() == 1);
var match2 =
myList.AsEnumerable().GroupBy(
record =>
new { PRODUCT = record.Product, SHIPPINGTIME = record.ShippingTime }).Where(z => z.Count() == 2);
var match3 =
myList.AsEnumerable().GroupBy(
record =>
new { PRODUCT = record.Product, SHIPPINGTIME = record.ShippingTime }).Where(z => z.Count() == 3);
var match4 =
myList.AsEnumerable().GroupBy(
record =>
new { PRODUCT = record.Product, SHIPPINGTIME = record.ShippingTime }).Where(z => z.Count() == 4);
var match5 =
myList.AsEnumerable().GroupBy(
record =>
new { PRODUCT = record.Product, SHIPPINGTIME = record.ShippingTime }).Where(z => z.Count() == 5);
// Get the total from each of these group where they match, throw the rest out.
foreach (var entry in sublist)
{
Console.WriteLine(entry);
}
Assert.That(sublist, Is.Not.Null);
}
}
// Supporting Class
public class MyClass
{
public int Id { get; set; }
public string Zone { get; set; }
public string Product { get; set; }
public string ShippingTime { get; set; }
}
I'm not sure I understood you right, but I think you want to do something like that:
myList.Select(record => record.Product)
.Distinct()
.Select(p => new
{
Product = p,
Zones = myList.Where(r => r.Product == p)
.Select(r => r.Zone)
.Distinct()
})
.GroupBy(an => an.Zones.Count())
var data = new[] {
new { Id = 0, Cat = 1, Price = 2 },
new { Id = 1, Cat = 1, Price = 10 },
new { Id = 2, Cat = 1, Price = 30 },
new { Id = 3, Cat = 2, Price = 50 },
new { Id = 4, Cat = 2, Price = 120 },
new { Id = 5, Cat = 2, Price = 200 },
new { Id = 6, Cat = 2, Price = 1024 },
};
var ranges = new[] { 10, 50, 100, 500 };
Needed output is grouped price count by equal or greater than the range used according categories.
(in one linq statement)
cat range count
-------------------------------------
1 10 2 (In 1. categories there is 2 item that price >= 10(range) [10;30])
2 10 4 (In 2. categories there is 4 item that price >= 10(range) [50;120;200;1024])
2 50 4 ....
2 100 3 ....
2 500 1 (In 2. categories there is 1 item that price >= 500(range) [1024])
Try this:
var data = new[] {
new { Id = 0, Cat = 1, Price = 2 },
new { Id = 1, Cat = 1, Price = 10 },
new { Id = 2, Cat = 1, Price = 30 },
new { Id = 3, Cat = 2, Price = 50 },
new { Id = 4, Cat = 2, Price = 120 },
new { Id = 5, Cat = 2, Price = 200 },
new { Id = 6, Cat = 2, Price = 1024 },
};
var ranges = new[] { 10, 50, 100, 500 };
var result = from r in ranges
from g in data
where g.Price >= r
select new {g.Cat, Price=r};
var groupedData =
from d in result
group d by new{d.Cat, d.Price} into g
select new{Cat=g.Key.Cat, Price=g.Key.Price, TotalCount=g.Count()};
This should work:
var values =
data.SelectMany(x => ranges.Where(y => x.Price >= y)
.Select(y => new { Record = x, Range = y }))
.GroupBy(x => new { Cat = x.Record.Cat, Range = x.Range })
.Select(x => new { Cat = x.Key.Cat, Range = x.Key.Range, Count = x.Count()});
Results:
{ Cat = 1, Range = 10, Count = 2 }
{ Cat = 2, Range = 10, Count = 4 }
{ Cat = 2, Range = 50, Count = 4 }
{ Cat = 2, Range = 100, Count = 3 }
{ Cat = 2, Range = 500, Count = 1 }