My table is:
id | globalId | taskStatus |
1 | 10 | New |
2 | 11 | New |
3 | 10 | InProgress |
4 | 12 | New |
I would like to have a linq query that returns me result with row 2.
Conditions to check in query
Want to ignore those records which have same globalId and if task status of any record is InProgress. So in this case as record with 1, 3 have same global id 10 but task status of record with id 3 is InProgress, so don't want any of the two records.
Also a check in where condition Id < 4
I have tried the below query
var result = (from meetings in db.Meetings
join taskStatus in db.TaskStatus on meeting.TaskStatusId equals taskStatus.TaskStatusId
where (taskStatus.Name == InternalTaskStatus.New || taskStatus.Name == InternalTaskStatus.ToBePlannedInFuture || taskStatus.Name == InternalTaskStatus.Failed)
&& meeting.CalendarEvent != CalendarEvents.Delete
&& meeting.StartDateTime >= planningPeriodStartDate && meeting.EndDateTime <= planningPeriodEndDate
group meeting by meeting.GlobalAppointmentId into m
select new
{
MeetingResult = m.FirstOrDefault()
}).FirstOrDefault();
In the above query I have added check for task status, want only records with taskStatus-New,Failed,ToBePlannedInFuture. But here I am getting wrong result in this case as per above table I am getting result with id 1.
The ideal way to approach this is to, split the requirement.
Requirement 1 : Ignore Items where id < 4
var step1 = testList.Where(x=>x.id<4);
Requirement 2: Ignore Groups of items with same globalId is same, and none of the elements in group has status in "InProgress"
var step2 = step1.GroupBy(x=>x.globalId)
.Where(x=>!x.Any(c=>c.taskStatus.Equals("InProgress")));
Now you need to flatten the group to get the result as IEnumerabble
var step3= step2.SelectMany(x=>x);
Putting it all together
var result = testList.Where(x=>x.id<4).GroupBy(x=>x.globalId)
.Where(x=>!x.Any(c=>c.taskStatus.Equals("InProgress")))
.SelectMany(x=>x);
public class test
{
public int id { get; set; }
public int globalId { get; set; }
public string taskStatus { get; set; }
}
public void SampleName()
{
List<test> testList = new List<test>()
{
new test() { id = 1, globalId = 10, taskStatus = "New"},
new test() { id = 2 , globalId = 11 , taskStatus = "New"},
new test() { id = 3 , globalId = 10 , taskStatus = "InProgress"},
new test() { id = 4 , globalId = 12 , taskStatus = "New"}
};
var result = testList.Where(q => testList.Count(a => a.globalId == q.globalId) == 1 && q.taskStatus != "InProgress" && q.id < 4).ToList();
}
Related
I have 2 lists. List A consists of this value,
status | level
-----------------
open | low
open | medium
open | high
closed | low
closed | medium
closed | high
List B consists of this value,
task | status | level
------------------------
A | open | low
B | open | medium
C | closed | high
D | closed | low
E | open | low
I want to do left join (all value inside list A must be in the new list), and count the number of tasks with related to status. I want the level value as well since it will be used later in my code. The expected output:
status | level | count
-------------------------
open | low | 2
open | medium | 1
open | high | 0
closed | low | 1
closed | medium | 0
closed | high | 1
I know there are many answers here which provides the ways to code, but I'm still stuck, because my code doesnt work, it seems like it does not do the group by method because when I count, the value shown is one for all status.
var joined3 = (from id1 in joined
join id2 in tr
on new { lev = id1.Key.ToString(), stat = id1.Value.ToString() } equals new { lev = id2.Level.ToString(), stat = id2.Status.ToString() } into grouped
from id2 in grouped.DefaultIfEmpty()
group id2 by new {level = id1.Key, status = id1.Value } into grouped
select new
{
level = grouped.Key.level,
status = grouped.Key.status,
count = grouped.Count()
}).ToList();
The problem is that because of the left-join semantics of DefaultIfEmpty(), you always have at least one row. So you need to add a predicate to the Count()
var joined3 = (
from id1 in joined
join id2 in tr
on new { lev = id1.Key, stat = id1.Value } equals new { lev = id2.Level, stat = id2.Status } into grouped
from id2 in grouped.DefaultIfEmpty()
group id2 by new {level = id1.Key, status = id1.Value } into grouped
select new
{
level = grouped.Key.level,
status = grouped.Key.status,
count = grouped.Count(id2 => id2.Key != null)
}).ToList();
Alternatively, a simpler method is: don't group, but instead use a correlated count of the other list
var joined3 = (
from id1 in joined
select new
{
level = id1.level,
status = id1.status,
count = tr.Count(id2 => id2.Key == id1.Key && id2.Value == id1.Value)
}).ToList();
I see no reason to use ToString here, and it is likely to impact performance. Key and Value should be the same type on each list/table respectively.
var list1 = new List<Type1>
{
new Type1() {Status = StatusEnum.Open, Level = LevelEnum.Low},
new Type1() {Status = StatusEnum.Open, Level = LevelEnum.Medium},
new Type1() {Status = StatusEnum.Open, Level = LevelEnum.High},
new Type1() {Status = StatusEnum.Closed, Level = LevelEnum.Low},
new Type1() {Status = StatusEnum.Closed, Level = LevelEnum.Medium},
new Type1() {Status = StatusEnum.Closed, Level = LevelEnum.High}
};
var list2 = new List<Type2>
{
new Type2() {TaskDescription = "A", Status = StatusEnum.Open, Level = LevelEnum.Low},
new Type2() {TaskDescription = "B", Status = StatusEnum.Open, Level = LevelEnum.Medium},
new Type2() {TaskDescription = "C", Status = StatusEnum.Closed, Level = LevelEnum.High},
new Type2() {TaskDescription = "D", Status = StatusEnum.Closed, Level = LevelEnum.Low},
new Type2() {TaskDescription = "E", Status = StatusEnum.Open, Level = LevelEnum.Low}
};
var list3 = new List<Type3>();
foreach (var t in list1)
{
list3.Add(new Type3()
{Level = t.Level, Status = t.Status, Count = list2.Count(x => x.Level == t.Level && x.Status == t.Status)});
}
foreach (var t in list3)
{
Console.WriteLine($"{t.Status}/{t.Level}/{t.Count}");
}
class Type1
{
public StatusEnum Status { get; set; }
public LevelEnum Level { get; set; }
}
class Type2 : Type1
{
public string TaskDescription { get; set; }
}
class Type3 : Type2
{
public int Count { get; set; }
}
public enum StatusEnum
{
Open,
Closed
}
public enum LevelEnum
{
Low,
Medium,
High
}
I have two lists:
var qtys = new List<InventoryQuantity>()
{
new InventoryQuantity() { WarehouseId = 1, QuantityInWarehouse = 0 },
new InventoryQuantity() { WarehouseId = 2, QuantityInWarehouse = 452 },
new InventoryQuantity() { WarehouseId = 3, QuantityInWarehouse = 184 },
new InventoryQuantity() { WarehouseId = 4, QuantityInWarehouse = 328 },
new InventoryQuantity() { WarehouseId = 5, QuantityInWarehouse = 0 },
};
var times = new List<WarehouseTransitTime>()
{
new WarehouseTransitTime() { WarehouseId = 1, TransitDays = 1 },
new WarehouseTransitTime() { WarehouseId = 2, TransitDays = 4 },
new WarehouseTransitTime() { WarehouseId = 3, TransitDays = 2 },
new WarehouseTransitTime() { WarehouseId = 4, TransitDays = 3 },
new WarehouseTransitTime() { WarehouseId = 5, TransitDays = 5 },
};
class InventoryQuantity
{
public int WarehouseId { get; set; }
public int QuantityInWarehouse { get; set; }
}
class WarehouseTransitTime
{
public int WarehouseId { get; set; }
public int TransitDays { get; set; }
}
I need to return the WarehouseId from qtys where the Quantity > 0 and the WarehouseId equals the minimum transit days WarehouseId in times.
I know I can do something like below but seems clunky and there must be an elegant solution.
public int NearestWarehouse()
{
var withQty = qtys.Where(i => i.QuantityInWarehouse > 0);
var orderedTransit = times.OrderBy(tt => tt.TransitDays).ToList();
//loop and compare
}
Example data:
qtys
WarehouseId | Quantity
1 | 0
2 | 452
3 | 184
4 | 328
5 | 0
times
WarehouseId | TransitTime
1 | 1
2 | 4
3 | 2
4 | 3
5 | 5
Expected output would be 3, because warehouse 3 has inventory and the shortest transit time (2)
It seems to me that the cleanest and simplest query is this:
var query =
from q in qtys
where q.QuantityInWarehouse > 0
join t in times on q.WarehouseId equals t.WarehouseId
orderby t.TransitDays
select q.WarehouseId;
var warehouseId = query.FirstOrDefault();
This gives me 3.
What you want is a group join:
Functional Syntax
var query1 = qtys.Where(q => q.QuantityInWarehouse > 0)
.GroupJoin(times, q => q.WarehouseId, t => t.WarehouseId, (q, t) => new { q.WarehouseId, TransitDays = t.DefaultIfEmpty().Min(grp => grp?.TransitDays) })
.OrderBy(g => g.TransitDays)
.FirstOrDefault();
Query Syntax
var query2 = from q in qtys
join t in times on q.WarehouseId equals t.WarehouseId into grp
where q.QuantityInWarehouse > 0
select new
{
q.WarehouseId,
TransitDays = grp.DefaultIfEmpty().Min(g => g?.TransitDays)
};
var result = query2.OrderBy(g => g.TransitDays)
.FirstOrDefault();
A group join will join two lists together on their corresponding keys--similar to a database join--and the associated values to those keys will be grouped into an enumerable. From that enumerable, you can derive the minimum value that you care about, TransitDays in this case.
There is no equivalent to "first or default" in query syntax. The easiest approach is just to apply the same OrderBy and FirstOrDefault against the query variable, as demonstrated above.
Well you mention an AND relation between the two, right?
I was thinking of databases with a forignkey... but Linq prety much does it if your lists aren't to big:
keys = qtys.Where(i => i.QuantityInWarehouse > 0).Select(i => i.WarehouseId).ToList();
// get the smallest not a list
var result = times.Where(tt => keys.Contains(tt.wharehouseId)).orderby(tt => tt.Transitdays).FirstOrDefault();
Otherwise you could have Dictionary with ID as key...
You can do it like this..
var withQty = (from q in qtys
join t in times on q.WarehouseId equals t.WarehouseId
where q.QuantityInWarehouse > 0
select new { q.WarehouseId, t.TransitDays })
.OrderBy(item => item.TransitDays).FirstOrDefault();
return withQty?.WarehouseId ?? 0;
I have a data set that looks like below:
Option | Year | Month | Value
-------+------+-------+------
1 | 2011 | 12 | 0
-------+------+-------+------
1 | 2011 | 11 | 1
-------+------+-------+------
2 | 2012 | 6 | 0
-------+------+-------+------
2 | 2012 | 7 | 0
-------+------+-------+------
1 | 2011 | 6 | 2
The result set I am looking for is below :
Option | Year | ChangedCount
-------+------+-------------
1 | 2011 | 3
-------+------+-------------
2 | 2012 | 0
-------+------+-------------
Changed Count represents , if the value has changed in the same year between different months . so say if the value of 06 month was 2 and then 07 it changed to 1 , then changed count will be 1 . If the value for two months remains the same , then changedCount is 0
Here is what I have written so far
var changes = from ord in resultSet
group ord by new
{
ord.Year,
ord.Month,
ord.Option,
ord.Value,
}
into g
select new
{
Year = g.Key.Year,
changed = g.Count(x => x.Value == 0)
+ g.Count(x => x.Value == 1)
+ g.Count(x => x.Value == 2)
};
How do I run comparison for previous value in column ?
{0,1,2} Map ENUM values
This is what I understand from your explanation:
class Record
{
public int Option { get; set; }
public int Year { get; set; }
public int Month { get; set; }
public int Value { get; set; }
}
var resultSet = new List<Record> {
new Record { Option=1, Year=2011, Month=12, Value=0 },
new Record { Option=1, Year=2011, Month=11, Value=1 },
new Record { Option=2, Year=2012, Month=6, Value=0 },
new Record { Option=2, Year=2012, Month=7, Value=0 },
new Record { Option=1, Year=2011, Month=6, Value=2 },
};
Helper Method to count changes:
public static int changeCount(List<Record> Records)
{
int previous = Records[0].Value;
var result_change = 0;
//i used sorted records by month you can do not this if order is not sensitive
foreach (var rec in Records.OrderBy(x=>x.Month))
{
if (rec.Value != previous)
{
result_change++;
}
previous = rec.Value;
}
return result_change;
}
and the actual code :
var changes = resultSet.GroupBy(x => new { x.Year }).Select(g => new
{
Year = g.Key.Year,
changed =changeCount( g.ToList()),
Option = g.First().Option
}).ToList();
Result :
2011,3,1
2012,0,2
Try:
var changes = from ord in resultSet
group ord by new
{
ord.Option,
ord.Year,
}
into g
select new
{
Option = g.Key.Option,
Year = g.Key.Year,
ChangedCount = g.Select(x => x.Value).Sum()
};
OR
resultSet
.GroupBy(x => new { x.Option, x.Year })
.Select(x => new { x.Key.Option, x.Key.Year, ChangedCount = x.Select(x => x.Value).Sum() });
I want to grouping phone number to the same section like this :
section | phone
1 | 1001
1 | 1002
2 | 1201
2 | 1202
and grouping them in like this :
section | phone
1 | 1001, 1002
2 | 1201, 1202
but i don't know syntax to groping them to that.
This code i do
var entgr = (from fx in MainOnline.MA_TelUsers
where fx.TE_SectionID != null
group fx by fx.TE_SectionID into id
from ids in id.DefaultIfEmpty()
select new
{
Section = ids.TE_SectionID,
TelPhone = ids.TE_Phone
});
How I grouping that and use it to join other table?
var entgr = (from fx in ph
group fx by fx.SectionId.ToString() into id
select new
{
Section = id.Key,
TelPhone = string.Join(", ",id.Select(s => s.PhoneNumber.ToString()))
});
try this query
var entgr = (from fx in MainOnline.MA_TelUsers
where fx.TE_SectionID != null
group fx by fx.TE_SectionID into ids
select new
{
Section = ids.TE_SectionID,
TelPhone =ids.Aggregate((a, b) =>
new {TelPhone = (a.TelPhone + ", " + b.TelPhone ) }).TelPhone
});
https://msdn.microsoft.com/en-us/library/vstudio/bb397696.aspx
See this link. If you want to it do it in single linq query then that I hope not possible.
But at evaluation time you can do like this
var ph = new List<Phone>();
ph.Add(new Phone() { SectionId = 1, PhoneNumber = 1001 });
ph.Add(new Phone() { SectionId = 1, PhoneNumber = 1002 });
ph.Add(new Phone() { SectionId = 2, PhoneNumber = 1201 });
ph.Add(new Phone() { SectionId = 2, PhoneNumber = 1202 });
var results = ph.GroupBy(i => i.SectionId).Select(i=> new {i.Key, i});
foreach (var phone in results)
{
int section = phone.Key;
string phoneNos = string.Join(",",phone.i.Select(i=>i.PhoneNumber));
}
I'm trying to get a count of parents with no children plus parents children. As I write this I realize it is better explained with code.. So, here it goes:
With these example types:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public string Description { get; set; }
}
And this data:
var customers = new List<Customer>
{
new Customer
{
Id = 2,
Name = "Jane Doe"
},
new Customer
{
Id = 1,
Name = "John Doe",
Orders = new List<Order>
{
new Order { Id = 342, Description = "Ordered a ball" },
new Order { Id = 345, Description = "Ordered a bat" }
}
}
};
// I'm trying to get a count of customer orders added with customers with no orders
// In the above data, I would expect a count of 3 as detailed below
//
// CId Name OId
// ---- -------- ----
// 2 Jane Doe
// 1 John Doe 342
// 1 John Doe 345
int customerAndOrdersCount = {linq call here}; // equals 3
I am trying to get a count of 3 back.
Thank you in advance for your help.
-Jessy Houle
ADDED AFTER:
I was truly impressed with all the great (and quick) answers. For others coming to this question, looking for a few options, here is a Unit Test with a few of the working examples from below.
[TestMethod]
public void TestSolutions()
{
var customers = GetCustomers(); // data from above
var count1 = customers.Select(customer => customer.Orders).Sum(orders => (orders != null) ? orders.Count() : 1);
var count2 = (from c in customers from o in (c.Orders ?? Enumerable.Empty<Order>() ).DefaultIfEmpty() select c).Count();
var count3 = customers.Sum(c => c.Orders == null ? 1 : c.Orders.Count());
var count4 = customers.Sum(c => c.Orders==null ? 1 : Math.Max(1, c.Orders.Count()));
Assert.AreEqual(3, count1);
Assert.AreEqual(3, count2);
Assert.AreEqual(3, count3);
Assert.AreEqual(3, count4);
}
Again, thank you all for your help!
How about
int customerAndOrdersCount = customers.Sum(c => c.Orders==null ? 1 : Math.Max(1, c.Orders.Count()));
If you would initialize that Order property with an empty list instead of a null, you could do:
int count =
(
from c in customers
from o in c.Orders.DefaultIfEmpty()
select c
).Count();
If you decide to keep the uninitialized property around, then instead do:
int count =
(
from c in customers
from o in (c.Orders ?? Enumerable.Empty<Order>() ).DefaultIfEmpty()
select c
).Count();
customers
.Select(customer => customer.Order)
.Sum(orders => (orders != null) ? orders.Count() : 1)
This works if you want to count "no orders" as 1 and count the orders otherwise:
int customerOrders = customers.Sum(c => c.Orders == null ? 1 : c.Orders.Count());
By the way, the question is very exemplary.
You probabbly searching for something like this:
customers.GroupBy(customer=>customer). //group by object iyself
Select(c=> //select
new
{
ID = c.Key.Id,
Name = c.Key.Name,
Count = (c.Key.Orders!=null)? c.Key.Orders.Count():0
}
);
var orderFreeCustomers = customers.Where(c=>c.Orders== null || c.Orders.Any()==false);
var totalOrders = customers.Where (c => c.Orders !=null).
Aggregate (0,(v,e)=>(v+e.Orders.Count) );
Result is the sum of those two values