Find missing item in a list grouped - c#

I have a list of objects dailySchedule with the properties Order, District, and other properties.
public class dailySchedule
{
public int Order { get; set; }
public string Title { get; }
public string District { get; }
....
}
the list is loaded with these values
Each district must have 6 orders,
var ordersValue = new List<int> {1, 2, 3, 4, 5, 6};
I want to find for each district, which order is missing.
The result must be
District 0 order {2,3,5,6}
District 12 order {5,6}
How can do that with linq c#?

This is a case for Except():
you can achieve your desired result by "substracting two lists" like so:
var required = new List<int>() {1, 2, 3, 4, 5, 6};
var groupedByDistrict = orders.GroupBy(x => x.District);
foreach (var group in groupedByDistrict)
{
var missing = required.Except(group.Select(x => x.Order).Distinct());
// Do something with that informaton here
}
This is mere non working pseudocode to get you on the right track
actually it may work, but i haven't tested it.

Try the below code which will give you result with each district and its missing orders
var allOrders = new List<int>() { 1, 2, 3, 4, 5, 6 };
var result = orders.GroupBy(gp => gp.District).Select(sl =>
new { District = sl.Key, Order = allOrders.Where(wh => sl.All(all => all.Order != wh)) });

Related

Find the max element in array field in the list of objects

Suppose I have a class Student with simple definition:
public string Name { get; set; }
public int[] Marks { get; set; }
Now I create a list of students:
List<Student> students = new List<User>();
students.Add(new Student("John", { 3, 4, 5, 5, 4 }));
students.Add(new Student("Adam", { 2, 5, 5, 1, 3 }));
students.Add(new Student("Katy", { 6, 3, 2, 2, 3 }));
Now, I need to create a LINQ query which will retrieve the best single mark among all of the students. In this case, it would be 6, because this is the highest value from all the arrays.
I came up with something like this:
var query =
from student in students
where student.Marks is not null
group student by student.Marks into studentMarks
group studentMarks.Key.Max() by studentMarks.Key.Max() into studentMarks
orderby studentMarks.Key descending
select studentMarks.Key;
Console.WriteLine(query.ElementAt(0)); // output: 6
Now, how can I write it in a better way, so that it just outputs single int, so I can simply say:
Console.WriteLine(query);
Use SelectMany:
int topMark = students.SelectMany(s => s.Marks).Max() // 6

Group data into a type and list of object members

Take the following example code:
public record ProductGroups(int size, List<Product> products);
public record Product(int size, int Id, string style);
queryResults = new List<Product>()
{
new Product(1, 1, "short sleeve"),
new Product(1, 2, "long sleeve"),
new Product(1, 3, "hoodie"),
new Product(2, 4, "med short sleeve"),
new Product(2, 5, "med long sleeve")
}
/* Want to turn queryResults into a list<ProductGroups> object that looks like this:
[
{
size: 1,
products: [{1, 1, "short sleeve"}, {1, 2, "long sleeve"} {1, 3, "hoodie}]
},
{
size: 2,
products: [{2, 4, "med short sleeve"}, {2, 5, "med long sleeve"}]
}
]
*/
I've tried multiple variations with GroupBy but didn't have any success achieving the desired format:
var productGroups = queryResults.GroupBy(x => x.size).ToList(); returns a List<IGrouping<int, Product>> which isn't quite what I want.
You could just group by Size and assign Products to the list of items in the group. The following returns an anonymous object:
var result = queryResults.GroupBy(r => r.size)
.Select(g => new { Size = g.Key, Products = g.ToList() });
If you need a concrete class/record ProductGroup then the query is very similar to the above:
var result = queryResults.GroupBy(r => r.size)
.Select(g => new ProductGroup(g.Key, g.ToList()));
public record ProductGroup(int Size, List<Product> Products);
But it also looks like your datatype matches Dictionary<int, List<Product>>. So you could use ToDictionary after grouping:
var result = queryResults.GroupBy(r => r.size)
.ToDictionary(r => r.Key, r => r.ToList());

Array looping and comparing if >2 differences

I'm trying to check the difference between a master list of items in c# vs an array of lists.
Not quite sure how to build this logic in an efficient way.
Example:
My master list of items is: var MasterOrderIDs = {1,2,3,4}
Then I have an Customer/Order object where it's storing the Customer ID along with its OrderID
CustomerID|OrderID
1 | 1
1 | 2
1 | 3
1 | 4
1 | 5
2 | 1
2 | 2
2 | 3
2 | 4
2 | 5
2 | 6
3 | 2
3 | 3
3 | 4
I want to return an array which has the CustomerID along with the OrderIDs where the difference of the MasterOrderIDs has a difference of less than 2.
Something like:
var OrderDifferences = new Dictionary<int, List<int>>();
So in the case of the Customer/Order, I want to return:
{[1, [5]], [3, [1]}
This is because for CustomerID 1, there is a Order ID 5, which is less than 2 differences. Likewise with CustomerID 3, there is a Order ID 1, which appears in MasterOrderIDs and is less than 2 differences.
How can I create such a check?
Ina real-life scenario I will have big data so wondering what would be the best efficient way of doing it.
Based on the information that we got I can think of two relatively small optimizations. So my disclaimer is that the basic approach is still brute force and maybe there is a smarter way to extract the information but we can still perform some checks in order to exclude some of the uneccessary data.
Small optimization 1
We are looking for Customers who compared to the the master list of orders have one more or one less order at most. In other words, based on your example for
var MasterOrderIDs = {1,2,3,4}
a Customer with 5 orders like customerOrders = { 7, 8, 9, 10, 11 } is still potentially valid but customer with 6 orders customerOrders = { 7, 8, 9, 10, 11, 12 } is not.
The same for the bottom number. A Customer with 3 orders is also potentially valid customerOrders = { 7, 8, 9 } but a customer with less the two orders customerOrders = { 7, 8 } is not.
So based on this we can perform our first small optimization filering customers who have more than MasterOrderIDs.Count() + 2 orders or with less than MasterOrderIDs.Count() - 2
Small optimization 2
Even if we are in the appropriate range of orders we want to make sure that our orderIds overlap. We can allow only 1 order which is present in one of the lists and not present in the other. Basically this is not exactly an optimization, but this is second criteria based on which we can construct our query.
Which is:
First seed some data:
class Order
{
public int CustomerId { get; set; }
public int OrderId { get; set; }
public static List<Order> Seed()
{
return new List<Order>
{
new Order { CustomerId = 1, OrderId = 1},
new Order { CustomerId = 1, OrderId = 2},
new Order { CustomerId = 1, OrderId = 3},
new Order { CustomerId = 1, OrderId = 4},
new Order { CustomerId = 1, OrderId = 5},
new Order { CustomerId = 2, OrderId = 1},
new Order { CustomerId = 2, OrderId = 2},
new Order { CustomerId = 2, OrderId = 3},
new Order { CustomerId = 2, OrderId = 4},
new Order { CustomerId = 2, OrderId = 5},
new Order { CustomerId = 2, OrderId = 6},
new Order { CustomerId = 3, OrderId = 2},
new Order { CustomerId = 3, OrderId = 3},
new Order { CustomerId = 3, OrderId = 4}
};
}
}
Then set the initial data:
var masterList = new List<int> { 1, 2, 3, 4 };
var upperBorder = masterList.Count() + 2;
var bottomBorder = masterList.Count() - 2;
var orders = Order.Seed();
And finally extract the records that we need:
var ordersWithinRange = orders
.GroupBy(o => o.CustomerId)
.Where(x => x.Count() < upperBorder && x.Count() > bottomBorder && x.Select(o => o.OrderId).Except(masterList).Concat(masterList.Except(x.Select(o => o.OrderId))).Count() < 2)
.ToDictionary(d => d.Key, d => d.Select(o => o.OrderId).Except(masterList).Concat(masterList.Except(d.Select(o => o.OrderId))).ToList());
Again. This will take a lot of computing time but I think it's a little bit faster than a sequence of for loops filtering one thing at a time.

How use contains with two dimenzional list

How find if any list in list of list contains all elements in another list?
Something like list list .contains(list), where list(list) is stanjaDKA, and list is tmpzaNormalanPrijelaz, and all list members are of type int?
I tried this but I get to much states in stanjaDKA at the end.
int indeks=stanjaDKA.FindIndex(x=>x.Equals(tmpzaNormalanPrijelaz));
if (indeks==-1 && tmpzaNormalanPrijelaz.Count>0)
{
stanjaDKA.Add(tmpzaNormalanPrijelaz);
}
How find if any X in list of X
Use LINQ's Any:
bool anyXInListOfX = myListOfX(x => someConditionOnX);
list contains all elements in another list
If you're not concerned about duplicate elements (i.e. if you're happy that {1} contains all the elements in {1, 1}), you can use LINQ's Except and check there is nothing left:
bool firstListContainsAllElementsInSecondList =
!mySecondList.Except(myFirstList).Any();
However, "any list contains all elements" is equivalent to "all lists don't contain all elements", and the don't cancels with the ! in the above, so in your case I'd do something like
if (stanjaDKA.All(l => tmpzaNormalanPrijelaz.Except(l).Any()))
{
stanjaDKA.Add(tmpzaNormalanPrijelaz);
}
This reads as "if all the lists in stanjaDKA are each missing at least one element in tmpzaNormalanPrijelaz, add tmpzaNormalanPrijelaz to stanjaDKA".
depending on what you are really want this will help you to do the mayor stuff
// Sample List<List<int>>
var listList = new List<List<int>>();
listList.Add(new List<int>() { 0, 1, 2, 3, 4 });
listList.Add(new List<int>() { 0, 1, 2, 3, 4 });
listList.Add(new List<int>() { 1, 1, 2, 3, 4 });
listList.Add(new List<int>() { 1, 1, 1, 1, 1 });
listList.Add(new List<int>() { 5, 6, 7, 8, 9 });
// the List you are seaching for
var searchList = new List<int>() { 10 };
foreach(List<int> list in listList)
{
var newList =list.Intersect(searchList);
if (newList.Count() == searchList.Count)
{
string elements = "";
foreach (int item in newList)
{
elements += item + " ";
}
Console.WriteLine(elements);
}
}
Console.ReadLine();
you should also take a look at this Link maybe you need it

LINQ for diffing sets

I have the following arrays:
var original= new int[] { 2, 1, 3 };
var target = new int[] { 1, 3, 4 };
enum Operation {Added,Removed}
I would like to execute a LINQ query that would return the following:
{{2,Removed},{4,Added}}
Limitation: I would like LINQ to perform this very efficiently and avoid and O(n^2) style algorithms.
Perhaps a LINQ solution is not the best option in this case.
This will produce a dictionary with the result that you want.
Dictionary<int, Operation> difference = new Dictionary<int,Operation>();
foreach (int value in original) {
difference.Add(value, Operation.Removed);
}
foreach (int value in target) {
if (difference.ContainsKey(value)) {
difference.Remove(value);
} else {
difference.Add(value, Operation.Added);
}
}
To keep the size of the dictionary down, perhaps it's possible to loop the enumerations in parallell. I'll have a look at that...
Edit:
Here it is:
Dictionary<int, Operation> difference = new Dictionary<int,Operation>();
IEnumerator<int> o = ((IEnumerable<int>)original).GetEnumerator();
IEnumerator<int> t = ((IEnumerable<int>)target).GetEnumerator();
bool oActive=true, tActive=true;
while (oActive || tActive) {
if (oActive && (oActive = o.MoveNext())) {
if (difference.ContainsKey(o.Current)) {
difference.Remove(o.Current);
} else {
difference.Add(o.Current, Operation.Removed);
}
}
if (tActive && (tActive = t.MoveNext())) {
if (difference.ContainsKey(t.Current)) {
difference.Remove(t.Current);
} else {
difference.Add(t.Current, Operation.Added);
}
}
}
Edit2:
I did some performance testing. The first version runs 10%-20% faster, both with sorted lists and randomly ordered lists.
I made lists with numbers from 1 to 100000, randomly skipping 10% of the numbers. On my machine the first version of the code matches the lists in about 16 ms.
enum Operation { Added, Removed, }
static void Main(string[] args)
{
var original = new int[] { 2, 1, 3 };
var target = new int[] { 1, 3, 4 };
var result = original.Except(target)
.Select(i => new { Value = i, Operation = Operation.Removed, })
.Concat(
target.Except(original)
.Select(i => new { Value = i, Operation = Operation.Added, })
);
foreach (var item in result)
Console.WriteLine("{0}, {1}", item.Value, item.Operation);
}
I don't think you can do this with LINQ using only a single pass given the stock LINQ extension methods but but might be able to code a custom extension method that will. Your trade off will likely be the loss of deferred execution. It would be interesting to compare the relative performance of both.
You are out of luck. If, as you stated in the comments, the lists are not sorted you can't compute the difference you seek in a single forward pass. Consider:
{ 1, 2, 3, 4, 5, 6, 7, ...
{ 1, 2, 3, 6, 7, 8, 9, ...
At the point where the first difference in encountered (4 vs. 6) it's impossible for you to determine if you are looking at the removal of 4 & 5 (as would be the case if both lists were monotonically increasing, or the insertion of 6, 7, 8, & 9 as would be the case if the lists continued like so:
{ 1, 2, 3, 4, 5, 6, 7, 8, 9,...
{ 1, 2, 3, 6, 7, 8, 9, 4, 5, 6, 7, 8, 9,...
This will achieve the result in a single pass, however I'm not sure of the complexity of the GroupBy operation.
var original= new int[] { 1, 2, 3 };
var target = new int[] { 1, 3, 4 };
var output = original.Select( i => new { I = i, L = "o" } )
.Concat( target.Select( i => new { I = i, L = "t" } ) )
.GroupBy( i => i.I ).Where( i => i.Count() == 1 )
.Select( i => new { I = i.Key, S = (i.ElementAt( 0 ).L == "o" ? Operation.Removed : Operation.Added) } );

Categories

Resources