I have a Collection:
Collection<DateOfValues> collectionDateOfValues;
...
I get a DateOfValues Instance - lets say dateOfValuesNew and want to iterate over the collection and overwrite only values that are different.
public class DateOfValues
{
{
this.Values = new Collection<SomeValue>();
}
public int id { get; set;}
public DateTime Start { get; set; }
public Collection<SomeValue> Values;
}
public class SomeValue
{
public int Id { get; set; }
public DateOfValues Date { get; set; }
public string Status { get; set; }
publi decimal StatusNumber { get; set; }
}
What I have done:
if (dateOfValuesNew != null)
{
foreach (var dateOfValues in collectionDateOfValues)
{
if (dateOfValues.Id == dateOfValuesNew.Id)
{
// Here Im sure to find the dateOfValues Instance I will work with.
}
}
}
But If I want to compare dateOfValues with dateOfValuesNew with foreach it is ugly and unreadable.
Is there any better way to do it?
The Start of DateOfValues can be changed. It is the easiest part - cause I can simply overwrite it.
The hard part is to compare SomeValue Collection. Every SomeValue can have changed Date and Status - this can be solved with overwriting too.
But SomeValue Collection can become new SomeValue or it can be deleted.
For example dateOfValues has 3 SomeValue in SomeValue Collection and dateOfValuesNew will have 4 or 2.
A bit tricky but this works:
class Program
{
static void Main(string[] args)
{
// Test
DateOfValues dov1 = new DateOfValues { Id = 1, Start = new DateTime(2011, 12, 01) };
dov1.AddSomeValue(1,"OK",2);
dov1.AddSomeValue(2,"Not OK",3);
dov1.AddSomeValue(3,"Not OK",4);
dov1.AddSomeValue(4,"Additional dov1",5);
DateOfValues dov2 = new DateOfValues { Id = 1, Start = new DateTime(2011, 12, 02) };
dov2.AddSomeValue(1, "OK", 2);
dov2.AddSomeValue(2, "Not OK", 4);
dov2.AddSomeValue(3, "OK", 1);
dov2.AddSomeValue(6, "Additional dov2", 15);
foreach (Tuple<SomeValue,SomeValue> difference in dov1.GetDifference(dov2))
{
if (difference.Item1 != null)
{
Console.WriteLine("Item1: Id:{0}; Status:{1}; Status Number:{2}",
difference.Item1.Id, difference.Item1.Status, difference.Item1.StatusNumber);
}
if (difference.Item2 != null)
{
Console.WriteLine("Item2: Id:{0}; Status:{1}; Status Number:{2}",
difference.Item2.Id, difference.Item2.Status, difference.Item2.StatusNumber);
}
Console.WriteLine("-------------------------------------------");
}
}
}
public class DateOfValues
{
public DateOfValues()
{
Values = new Collection<SomeValue>();
}
public int Id { get; set; }
public DateTime Start { get; set; }
public Collection<SomeValue> Values;
public void AddSomeValue(int id, string status, decimal statusNumber)
{
Values.Add(new SomeValue{Date = this,Id = id,Status = status,StatusNumber = statusNumber});
}
public IEnumerable<Tuple<SomeValue, SomeValue>> GetDifference(DateOfValues other)
{
IEnumerable<SomeValue> notMatching = Values.Where(v => !other.Values.Any(o => v.Equals(o)))
.Union(other.Values.Where(v=> !Values.Any(o=> v.Equals(o)))).Distinct();
return notMatching
.GroupBy(x => x.Id)
.Select(x =>
new Tuple<SomeValue, SomeValue>(
x.FirstOrDefault(y => y.Date == this), x.FirstOrDefault(y => y.Date == other)));
}
}
public class SomeValue : IEquatable<SomeValue>
{
public int Id { get; set; }
public DateOfValues Date { get; set; }
public string Status { get; set; }
public decimal StatusNumber { get; set; }
public bool Equals(SomeValue other)
{
return other.Id == Id && other.Status == Status && other.StatusNumber == StatusNumber;
}
}
Output:
Item1: Id:2; Status:Not OK; Status Number:3
Item2: Id:2; Status:Not OK; Status Number:4
-------------------------------------------
Item1: Id:3; Status:Not OK; Status Number:4
Item2: Id:3; Status:OK; Status Number:1
-------------------------------------------
Item1: Id:4; Status:Additional dov1; Status Number:5
-------------------------------------------
Item2: Id:6; Status:Additional dov2; Status Number:15
-------------------------------------------
Edit
Alternative you could use an EqualityComparer:
public class DateOfValues
{
public DateOfValues()
{
Values = new Collection<SomeValue>();
}
public int Id { get; set; }
public DateTime Start { get; set; }
public Collection<SomeValue> Values;
public void AddSomeValue(int id, string status, decimal statusNumber)
{
Values.Add(new SomeValue { Date = this, Id = id, Status = status, StatusNumber = statusNumber });
}
public IEnumerable<Tuple<SomeValue, SomeValue>> GetDifference(DateOfValues other)
{
var notMatching = Values.Except(other.Values, new SomeValueComparer())
.Union(other.Values.Except(Values,new SomeValueComparer()));
return notMatching
.GroupBy(x => x.Id)
.Select(x =>
new Tuple<SomeValue, SomeValue>(
x.FirstOrDefault(y => y.Date == this), x.FirstOrDefault(y => y.Date == other)));
}
}
public class SomeValueComparer : IEqualityComparer<SomeValue>
{
public bool Equals(SomeValue x, SomeValue y)
{
return
x.Id == y.Id &&
x.Status == y.Status &&
x.StatusNumber == y.StatusNumber;
}
public int GetHashCode(SomeValue obj)
{
return obj.GetHashCode();
}
}
public class SomeValue
{
public int Id { get; set; }
public DateOfValues Date { get; set; }
public string Status { get; set; }
public decimal StatusNumber { get; set; }
public override int GetHashCode()
{
return string.Format("{0}{1}{2}",Id,Status,StatusNumber).GetHashCode();
// or a better method to get a hashcode
}
}
Related
I have an object below:
public class SubjectCategory : BaseModel
{
public decimal ParentSubjectCategoryId { get; set; }
public bool IsEnable { get; set; }
public virtual List<Subject>? Subjects { get; set; }
public virtual List<SubjectCategory>? ChildrenCategoris { get; set; }
public virtual SubjectCategory? ParentCategory { get; set; }
}
I get lists of subjectCategories from database (Picture Below).
I wrote a method that it adds ChildrenCategoris inside the categories which their ParentSubjectCategory Id is NULL or 0, but the problem is it is only works for the first level of tree!
public List<SubjectCategory> GetAllSubjectCategories()
{
var res = _subjectCategoryRepository.Select(new SubjectCategory {}).ToList();
List<SubjectCategory> newSubjectCategory = new List<SubjectCategory>();
foreach (var item in res)
{
if(item.ParentSubjectCategoryId != 0)
{
var a = newSubjectCategory.Where(sc => sc.Id ==
item.ParentSubjectCategoryId).FirstOrDefault();
if(a.ChildrenCategoris == null)
{
newSubjectCategory.Where(sc => sc.Id ==
item.ParentSubjectCategoryId).FirstOrDefault().ChildrenCategoris = new List<SubjectCategory>() { item};
}
else
{
newSubjectCategory.Where(sc => sc.Id == item.ParentSubjectCategoryId).FirstOrDefault().ChildrenCategoris.Add(item);
}
}
else
{
newSubjectCategory.Add(item);
}
}
return res;
}
But every child can have many ChildrenCategoris and their children can have many ChildrenCategoris as well and again.
the loop count is unknown.
how can I have a list with multiple children with C# ?
Is there any way i can get number of child with max parent id and list the result with linq?
I'm trying to bring the total of values by Status, but i can only get the last one from the child
what I have done so far:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var lstRfq = new List<RfqEvents>()
{
new RfqEvents(1,1,DateTime.Parse("2021-05-06 03:00:00+00"),1),
new RfqEvents(2,2,DateTime.Parse("2021-05-06 03:00:00+00"),1),
new RfqEvents(3,2,DateTime.Parse("2021-05-06 03:00:00+00"),1),
new RfqEvents(4,3,DateTime.Parse("2021-05-06 00:00:00+00"),2),
new RfqEvents(5,4,DateTime.Parse("2021-05-06 00:00:00+00"),2),
new RfqEvents(6,5,DateTime.Parse("2021-05-06 00:00:00+00"),2),
new RfqEvents(7,5,DateTime.Parse("2021-05-06 00:00:00+00"),2),
new RfqEvents(8,5,DateTime.Parse("2021-05-06 00:00:00+00"),3),
new RfqEvents(9,6,DateTime.Parse("2021-05-06 00:00:00+00"),3),
new RfqEvents(10,6,DateTime.Parse("2021-05-06 00:00:00+00"),3),
};
var subquery = from c in lstRfq
group c by c.RfqId into g
select new InternalStatusInformations
{
RfqId = g.Key,
RfqEventId = g.Max(a => a.Id),
StatusId = g.Select(p => p.Status).FirstOrDefault()
};
var sss = from d in lstRfq.Where(p=> subquery.Select(p=>p.RfqEventId).Contains(p.Id))
group d by d.Status into z
select new InternalStatusInformations
{
StatusId = z.Key,
Total = z.Count(),
Past = z.Where(p => p.DueDate.HasValue && p.DueDate.Value.Date < DateTime.Now.Date).Count(),
Future = z.Where(p => p.DueDate.HasValue && p.DueDate.Value.Date > DateTime.Now.Date).Count(),
Today = z.Where(p => p.DueDate.HasValue && p.DueDate.Value.Date == DateTime.Now.Date).Count(),
FiveDays = z.Where(p => (p.DueDate.HasValue && p.DueDate.Value.Date > DateTime.Now.Date) && p.DueDate.HasValue && p.DueDate.Value.Date < DateTime.Now.Date.AddDays(5)).Count(),
};
//expected: Status 1: 2 values
// Status 2: 3 values
// Status 3: 2 value
//output: Status 1: 2 values
// Status 2: 2 values
// Status 3: 2 values
sss.Dump();
}
public class InternalStatusInformations
{
public int RfqEventId { get; set; }
public int RfqId { get; set; }
public int StatusId { get; set; }
public int Future { get; set; }
public int Past { get; set; }
public int Today { get; set; }
public int FiveDays { get; set; }
public int Total { get; set; }
public DateTime? DueDate { get; set; }
}
public class RfqEvents
{
public RfqEvents(int id, int rfqId, DateTime? dueDate, int status)
{
Id = id;
RfqId = rfqId;
DueDate = dueDate;
Status = status;
}
public int Id { get; set; }
public DateTime? DueDate { get; set; }
public int RfqId { get; set; }
public int Status { get; set; }
}
}
https://dotnetfiddle.net/YoRsIG
but something is not right with the results, could you guys help me?
If you just want to count the number of distinct RfqId values in each status, this should do it:
var pairs = lstRfq
.GroupBy(evt => evt.Status)
.Select(grouping =>
{
var status = grouping.Key;
var count = grouping
.Select(evt => evt.RfqId)
.Distinct()
.Count();
return (status, count);
});
foreach ((var status, var count) in pairs)
{
Console.WriteLine($"Status {status}: {count} values");
}
Output is:
Status 1: 2 values
Status 2: 3 values
Status 3: 2 values
i have table looks like below
ID | Reason | PrID
-----------------
1 abc null
2 dhe null
3 aerc 1
4 dwes 2
5 adfje 1
i have class
public class Reason
{
public int Id { get; set; }
public string Reson{ get; set; }
public List<SecondryReason> SecReason{ get; set; }
public int? PrimaryId { get; set; }
}
public class SecondryReason
{
public int Id { get; set; }
public string Reason { get; set; }
public int PrimaryReasonId { get; set; }
}
I want this to be displayed in hierarchy level
if the prid is Null need to treat this as the parent remaining all child
i am trying Linq and unable to achieve this
Suggest me how to do this in an easy way in linq
So: You have a list/enumerable of type , whereof the SecReason List property is null. Then, using linq you want a list, were the only the "root" reasons remain, but the Sub-reasons got put in the lists, but as type SecondaryReason?
If so, I found this way to do it (linq and foreach):
static IEnumerable<Reason> GetReasonsGrouped(List<Reason> reasons)
{
var result = reasons.Where(x => x.PrimaryId == null);
foreach (var item in result)
{
item.SecReason = reasons.Where(x => x.PrimaryId == item.Id)
.Select(x => new SecondryReason()
{ Id = x.Id,
ReasonName = x.ReasonName,
PrimaryReasonId = item.Id
})
.ToList();
}
return result;
}
Or just linq, but harder to read:
var result = reasons.Where(x => x.PrimaryId == null)
.Select(x =>
{
x.SecReason = reasons.Where(r => x.PrimaryId == x.Id)
.Select(r => new SecondryReason()
{
Id = r.Id,
ReasonName = x.ReasonName,
PrimaryReasonId = x.Id
})
.ToList();
return x;
});
Not sure if linq will be the best solution, here is my proposed changes and method to get an Hierarchy type:
public class Reason
{
public int Id { get; set; }
public string Reson { get; set; }
public List<Reason> SecReason { get; set; }
public int? PrimaryId { get; set; }
//Adds child to this reason object or any of its children/grandchildren/... identified by primaryId
public bool addChild(int primaryId, Reason newChildNode)
{
if (Id.Equals(primaryId))
{
addChild(newChildNode);
return true;
}
else
{
if (SecReason != null)
{
foreach (Reason child in SecReason)
{
if (child.addChild(primaryId, newChildNode))
return true;
}
}
}
return false;
}
public void addChild(Reason child)
{
if (SecReason == null) SecReason = new List<Reason>();
SecReason.Add(child);
}
}
private List<Reason> GetReasonsHierarchy(List<Reason> reasons)
{
List<Reason> reasonsHierarchy = new List<Reason>();
foreach (Reason r in reasons)
{
bool parentFound = false;
if (r.PrimaryId != null)
{
foreach (Reason parent in reasonsHierarchy)
{
parentFound = parent.addChild(r.PrimaryId.Value, r);
if (parentFound) break;
}
}
if (!parentFound) reasonsHierarchy.Add(r);
}
return reasonsHierarchy;
}
I am working with some code that uses Columns.Add in conjunction with a lambda expression and would like to understand why/how it works. Here is a code snippet:
public ReportGrid(List<ReportRowDataContract> items)
: base(items)
{
if (items[0].ReportData1 != null)
{
if (items[0].ReportData1.DecimalValue != null)
{
Columns.Add(m => m.ReportData1.DecimalValue).Titled(items[0].ReportData1.Name).Encoded(false).
Sanitized(false).RenderValueAs(
m => (string.IsNullOrEmpty(#m.ReportData1.DisplayFormat)) ? Convert.ToDecimal(#m.ReportData1.DecimalValue).ToString("N") : Convert.ToDecimal(#m.ReportData1.DecimalValue).ToString(#m.ReportData1.DisplayFormat));
if (items[0].ReportData1.SumValue || items[0].ReportData1.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = items[0].ReportData1.Name,
AvgValue = items[0].ReportData1.AvgValue,
DecimalValue = 0
});
}
}
else if (items[0].ReportData1.IntValue != null)
{
Columns.Add(m => m.ReportData1.IntValue).Titled(items[0].ReportData1.Name);
if (items[0].ReportData1.SumValue || items[0].ReportData1.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = items[0].ReportData1.Name,
AvgValue = items[0].ReportData1.AvgValue,
IntValue = 0
});
}
}
else
{
Columns.Add(m => m.ReportData1.StringValue).Titled(items[0].ReportData1.Name);
}
}
if (items[0].ReportData2 != null)
{
if (items[0].ReportData2.DecimalValue != null)
{
Columns.Add(m => m.ReportData2.DecimalValue).Titled(items[0].ReportData2.Name).Encoded(false).
Sanitized(false).RenderValueAs(
m => (string.IsNullOrEmpty(#m.ReportData2.DisplayFormat)) ? Convert.ToDecimal(#m.ReportData2.DecimalValue).ToString("N") : Convert.ToDecimal(#m.ReportData2.DecimalValue).ToString(#m.ReportData1.DisplayFormat));
if (items[0].ReportData2.SumValue || items[0].ReportData2.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = items[0].ReportData2.Name,
AvgValue = items[0].ReportData2.AvgValue,
DecimalValue = 0
});
}
}
else if (items[0].ReportData2.IntValue != null)
{
Columns.Add(m => m.ReportData2.IntValue).Titled(items[0].ReportData2.Name);
if (items[0].ReportData2.SumValue || items[0].ReportData2.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = items[0].ReportData2.Name,
AvgValue = items[0].ReportData2.AvgValue,
IntValue = 0
});
}
}
else
{
Columns.Add(m => m.ReportData2.StringValue).Titled(items[0].ReportData2.Name);
}
}
This method consists of code that repeats itself out to ReportData6, changing only the ReportData field name with each repetition.
Here is the ReportRowDataContract class:
public class ReportRowDataContract
{
public ReportDataDataContract ReportData1 { get; set; }
public ReportDataDataContract ReportData2 { get; set; }
public ReportDataDataContract ReportData3 { get; set; }
public ReportDataDataContract ReportData4 { get; set; }
public ReportDataDataContract ReportData5 { get; set; }
public ReportDataDataContract ReportData6 { get; set; }
// an indexed property - for accessing report data fields by index
public ReportDataDataContract this[int i]
{
get
{
return new ReportDataDataContract[]
{
ReportData1,
ReportData2,
ReportData3,
ReportData4,
ReportData5,
ReportData6
}[i];
}
}
public int GetReportDataFieldCount()
{
return 6;
}
}
Unfortunately, I cannot change the structure of this class so I'm trying to convert the first code block into a method that loops. However, I'm stuck on what the Columns.Add is actually adding to.
Here is my code thus far:
public ReportGrid(List<ReportRowDataContract> items)
: base(items)
{
// get count of how many fields exist in ReportRowDataContract
int reportDataFieldCount = (new ReportRowDataContract()).GetReportDataFieldCount();
// create columns for grid for each field in ReportRowDataContract
//foreach (ReportRowDataContract item in items)
//{
int i = 0;
while (i < reportDataFieldCount)
{
AddGridColumn(items[0][i]);
i++;
}
//}
}
private void AddGridColumn(ReportDataDataContract reportColumn)
{
if (reportColumn != null)
{
if (reportColumn.DecimalValue != null)
{
Columns.Add(m => m.ReportData1.DecimalValue).Titled(reportColumn.Name).Encoded(false).
Sanitized(false).RenderValueAs(
m => (string.IsNullOrEmpty(#m.ReportData1.DisplayFormat)) ?
Convert.ToDecimal(#m.ReportData1.DecimalValue).ToString("N") :
Convert.ToDecimal(#m.ReportData1.DecimalValue).ToString(#m.ReportData1.DisplayFormat));
if (reportColumn.SumValue || reportColumn.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = reportColumn.Name,
AvgValue = reportColumn.AvgValue,
DecimalValue = 0
});
}
}
else if (reportColumn.IntValue != null)
{
Columns.Add(m => m.ReportData1.IntValue).Titled(reportColumn.Name);
if (reportColumn.SumValue || reportColumn.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = reportColumn.Name,
AvgValue = reportColumn.AvgValue,
IntValue = 0
});
}
}
else
{
Columns.Add(m => m.ReportData1.StringValue).Titled(reportColumn.Name);
}
}
}
In the AddGridColumn method the part that is problematic is m => m.ReportData1. It's not dynamic so the first loop through is fine but on the second loop through an exception is thrown: System.ArgumentException {"Column 'ReportData1.StringValue' already exist in the grid"}. I know the m.ReportData1 needs to be changed or the approach modified...just don't know how to go about it.
Edit #1: Disabled the foreach per VitezslavSimon's insight. The same exception message is being thrown.
Edit #2: Base class of grid (I think).
public class Grid<T> : GridBase<T>, IGrid where T : class
{
public Grid(IEnumerable<T> items);
public Grid(IQueryable<T> items);
public IGridColumnCollection<T> Columns { get; }
public bool DefaultFilteringEnabled { get; set; }
public bool DefaultSortEnabled { get; set; }
public virtual int DisplayingItemsCount { get; }
public bool EnablePaging { get; set; }
public string Language { get; set; }
public IGridPager Pager { get; set; }
public GridRenderOptions RenderOptions { get; set; }
public ISanitizer Sanitizer { get; set; }
public override IGridSettingsProvider Settings { get; set; }
public virtual void AutoGenerateColumns();
protected internal virtual IEnumerable<T> GetItemsToDisplay();
}
It seems you need to add columns only once there. Try to help it by declaring a flag there. It also depends how your grid component is working behind.
Your code with draft of proposed change:
public ReportGrid(List<ReportRowDataContract> items)
: base(items)
{
// get count of how many fields exist in ReportRowDataContract
int reportDataFieldCount = (new ReportRowDataContract()).GetReportDataFieldCount();
// create columns for grid for each field in ReportRowDataContract
bool flag = true;
foreach (ReportRowDataContract item in items)
{
int i = 0;
if (flag) {
while (i < reportDataFieldCount)
{
AddGridColumn(items[0][i]);
i++;
}
flag = false;
}
}
}
How do I filter an item from a list based on two different columns one being a number(smallest number) using LINQ C#?
public class Line
{
public int Id { get; set; }
public List<LineItem> LineItems { get; set; }
public Line()
{
LineItems = new List<LineItem> {
new LineItem{Num = 1, Name="i", Qty = 0, Active = false},
new LineItem{Num = 2, Name="j", Qty = 2,Active = false},
new LineItem{Num = 3, Name="k", Qty = 3,Active = false},
};
}
}
public class LineItem
{
public int Num { get; set; }
public string Name { get; set; }
public int Qty { get; set; }
public bool Active { get; set; }
}
I want to filter this list and get a LineItem based on Qty = 0 and smallest num value.
Try filtering by Qty == 0, sorting according to Num and keep the first one:
var lineItem = LineItems.Where(l => l.Qty == 0).OrderBy(l => l.Num).FirstOrDefault();
or just keep the first that Qty equals to 0 and Num equals to minimum possible:
var minNum = LineItems.Where(l => l.Qty == 0).Min(l => l.Num);
var lineItem = LineItems.FirstOrDefault(l => l.Qty == 0 && l.Num == minNum);
You could try to get the min value for Qty is 0 and order by Num in ascending mode, then taking the first item. For sample:
var item = LineItems.Where(x => x.Qty == 0).OrderBy(x => x.Num).First();
If you have your LineItem class implement IComparable<T>, then you can do something like this:
public class LineItem : IComparable<LineItem>
{
public int Num { get; set; }
public string Name { get; set; }
public int Qty { get; set; }
public bool Active { get; set; }
public int CompareTo(LineItem other)
{
if (other.Num > this.Num)
return -1;
else if (other.Num == this.Num)
return 0;
else
return 1;
}
}
then
var item = l.LineItems.Where(p => p.Qty == 0).Min();