How to get maximum Time group by id c# - c#

From the client API I get list of data. I have following code, this is a sample code, just need to create sample list to show client data.
class Program
{
static void Main(string[] args)
{
List<Author> authors = new List<Author>
{
new Author { Id = "100", Status = "Time (02:15 PM)" , Value = "A" , Test = "B" },
new Author { Id = "101", Status = "Time (02:16 PM)" , Value = "A" , Test = "B"},
new Author { Id = "100", Status = "Time (02:10 PM)" , Value = "A" , Test = "B"},
new Author { Id = "100", Status = "Time (11:15 AM)" , Value = "A" , Test = "B"},
new Author { Id = "101", Status = "Time (03:40 PM)" , Value = "A" , Test = "B"},
new Author { Id = "100", Status = "Time (02:15 AM)" , Value = "A" , Test = "B"}
};
var aa = authors.ToList();
}
}
public class Author
{
public string Id { get; set; }
public string Status { get; set; }
public string Value { get; set; }
public string Test { get; set; }
}
Here I need to get distinct Id with their maximum Status by its time value.
From the output I should need to get this.
Id = 100, its maximum Status is : Time (02:15 PM)
Id = 101, its maximum Status is : Time (03:40 PM)
How can I do this, its looks something mad, but I need to do this. how can I do this
Updated :
DateTime dt1 = DateTime.Now ;
List<Author> authors = new List<Author>
{
new Author { Id = "100", Test = "A", dateTime = dt1.AddMinutes(-5) },
new Author { Id = "101", Test = "K", dateTime = dt1.AddMinutes(-8) },
new Author { Id = "100", Test = "C", dateTime = dt1.AddMinutes(-6) },
new Author { Id = "100", Test = "D" , dateTime = dt1.AddMinutes(-18)},
new Author { Id = "101", Status = "G" , dateTime = dt1.AddMinutes(-6)},
new Author { Id = "100", Status = "Q" , dateTime = dt1.AddMinutes(-3)}
};
Updated my question, I added
I added another field called datetime for the ease of the Get max value. Then how can I get maximum datetime for each Id
Output need there two records data,
Id = "100", Status = "Q" , dateTime = dt1.AddMinutes(-3)
Id = "101", Status = "G" , dateTime = dt1.AddMinutes(-6)

I didn't try but maybe you can split (03:40 PM) part in all statuses and with CultureInfo.InvariantCulture's help you can convert this string to datetime. Then with linq's help you can try something like this:
var list = authors
.OrderBy(x => x.Id)
.ThenByDescending(x => x.Status)
.GroupBy(x => x.Id)
.Select(y=> y.FirstOrDefault())
.ToList();

authors.GroupBy(x => int.Parse(x.Id)).Select(x => x.OrderByDescending(y=> DateTime.Parse(y.Status.Substring(6, 8))
).FirstOrDefault());

Apparently Status is a string, but you want to interpret it as a DateTime.
If you can, add a property to your Author:
class Author
{
...
public Datetime StatusTime => // use property Status to extract the Time
}
I assume I don't have to write how you get the Time from the Status string. If that is a challenge for you this will be a nice exercise for you.
If you can't change class Author, consider to create an extension method, so that you can use it, as if it is a method in class Author.
If you are not familiar with extension methods, see Extension methods demystified
public static class AuthorExtensions
{
public static DateTime GetStatusTime(this Author author)
{
// TODO: remove the "Time = ( " part and use DateTime.Parse,
}
}
Usage:
Author author = ...
DateTime time = author.GetStatusTime();
Once you've got a method like this, the LINQ will be easy: make groups of Authors with same Id, and from every Author in one group, take the Author with the highest value for StatusTime:
IEnumerable<Author> authors = ...
// Make groups of Authors with same Id:
var result = authors.GroupBy(author => author.Id,
// parameter resultSelector, from every Id, and authors with this Id,
// select the author with the highest StatusTime:
(authorId, authorsWithThisId) => authorWithThisId
.OrderBy(author => author.GetStatusTime())
.FirstOrDefault());
It is a bit of a waste, to order the ten Authors in a group if you will be using only the first Author. If performance is an issue, consider to use Aggregate:
(authorId, authorsWithThisId) => authorWithThisId.Aggregate(
(selectedAuthor, nextAuthor) => (nextAuthor.GetStatusTime() > selectedAuthor.GetStatusTime()) ?
nextAuthor : selectedAuthor);
This will still translate the Status into StatusTime several times, so the optimal method:
var result = authors.Select(author => new
{
Id = author.Id,
StatusTime = author.GetStatusTime(),
})
.GroupBy(author => author.Id)
.Select(authorGroup => authorGroup.Aggregate(
(x, y) => (x.StatusTime > y.StatusTime) ? x, y);

Related

LINQ - Condition with .Contains() is not working as expected

I cannot seem to get the desirable filtered result from my query.
Data
public class fdp_1115
{
public string Id{ get; set; }
public string Number{ get; set; }
public string Type{ get; set; }
}
List<fdp_1115> fdpList = new List<fdp_1115>
{
new fdp_1115 { Id = "1", Number = "Lot123", Type = "D14MWT" },
new fdp_1115 { Id = "2", Number = "Lot123", Type = "E12WBC7W1" }
};
List<string> searchValues = new List<string> { "MLE12WBC7W1 A R" };
LINQ:
var LocType = fdpList.FirstOrDefault(d => searchValues.Any(s => d.Type.Contains(s)));
if (LocType != null)
{
Console.WriteLine("Matching record found:");
Console.WriteLine($"Id: {LocType.Id}, Number: {LocType.Number}, Type: {LocType.Type}");
}
else
{
Console.WriteLine("No matching records found.");
}
The result I wanted is:
Matching record found:
Id: 2, Number: Lot123, Type: E12WBC7W1
But I got "No matching records found." which indicates that LocType == null.
I already tried trimming and ignoring case sensitive:
var LocType = fdpList.FirstOrDefault(d => searchValues.Any(s => d.Type.Contains(s.Trim().Replace(" ", ""))));
var LocType = fdpList.FirstOrDefault(d => searchValues.Any(s => d.Type.Contains(s, StringComparison.InvariantCultureIgnoreCase)));
But still no luck. Any idea how do I match "MLE12WBC7W1 A R" with "E12WBC7W1"?
You have your contains the other way around.
d.Type = "E12WBC7W1"
and
s = "MLE12WBC7W1 A R"
Then "E12WBC7W1" does not Contains "MLE12WBC7W1 A R"
It is the other way around.
var LocType = fdpList.FirstOrDefault(d => searchValues.Any(s => s.Contains(d.Type)));
Your current logic checks whether there is any object with Type value that contains the value for each string in the searchValues array.
From your requirement:
You want to filter the object that fulfills there is any string in searchValues containing the value of Type.
Thus it should be:
var LocType = fdpList.FirstOrDefault(d => searchValues.Any(s => s.Contains(d.Type)));

How to choose certain values from ICollection to IDictionary, using LINQ

I have IDictionary<int,bool?> where int - id, bool? - state (true,false,null)
So i need to filter ICollection of objects, where i should compare internal id with id of my IDictionary and if IDs are the same and the state is true - i should select this element (using LINQ)
I tried: incomeCollection.Values.Select(x=>x.InternalId.Equals(dataFromDictionary.Keys.Any)).Select(h=> new Item){Item = h.Name}
but it does not works. I need to check all collection and select elements, which satisfy the condition above using LINQ. How can i do this?
The dictionary has a handy TryGetValue method allowing to look up an entry quickly.
class Income
{
public int InternalId { get; set; }
public string Name { get; set; }
}
var dictionary = new Dictionary<int,bool?>
{
{1, false},
{2, true},
{3, null},
};
var incomeCollection = new List<Income>
{
new Income { InternalId = 1, Name = "A" },
new Income { InternalId = 2, Name = "B" },
new Income { InternalId = 3, Name = "C" },
new Income { InternalId = 4, Name = "D" },
};
var result = incomeCollection.Where(x =>
dictionary.TryGetValue(x.InternalId, out var status) && status == true)
.Select(h=> new {Item = h.Name});
This is better than your first approach using dataFromDictionary.Keys.Any which does not take advantage of the Dictionary feature of quick lookup.
You can use Any()
var result = list.Where(x=>dictionary.Keys.Any(c=>c.Equals(x.Id)) && x.State.HasValue && x.State==true).Select(x=>x);
You can also use Join.
var result = list.Join(dictionary,
l=>l.Id,
d=>d.Key,(l,d)=>l)
.Where(x=>x.State.HasValue && x.State==true).Select(x=>x);
For example,
var dictionary = new Dictionary<int,bool?>
{
[1] = true,
[3] = true,
[35] = false
};
var list = new List<Person>
{
new Person{Name="Jia", Id=1,State=false},
new Person{Name="Aami", Id=3,State=true},
new Person{Name="Anu", Id=35,State=null},
};
Output
Aami 3 True
ToDictionary method can be used as shown in the below example.
In order to select only few values from ICollection you can use where clause.
List<Package> packages =
new List<Package>
{ new Package { Company = "Coho Vineyard", Weight = 25.2, TrackingNumber = 89453312L },
new Package { Company = "Lucerne Publishing", Weight = 18.7, TrackingNumber = 89112755L },
new Package { Company = "Wingtip Toys", Weight = 6.0, TrackingNumber = 299456122L },
new Package { Company = "Adventure Works", Weight = 33.8, TrackingNumber = 4665518773L } };
// Create a Dictionary of Package objects,
// using TrackingNumber as the key.
Dictionary<long, Package> dictionary =
packages.ToDictionary(p => p.TrackingNumber);
foreach (KeyValuePair<long, Package> kvp in dictionary)
{
Console.WriteLine(
"Key {0}: {1}, {2} pounds",
kvp.Key,
kvp.Value.Company,
kvp.Value.Weight);
}

many to many select list data

I would like to list the data with linq by combining the tables shown in the picture
expected result
using (CrmContext db = new Models.CrmContext())
{
_result = (from ct in db.CustomerDefination
join authorizedin db.authorized
on ct.customerId equals authorized.authorized
join authorizedDefination in db.authorizedDefination
on authorized.authorizedId equals authorizedDefination .authorizedId
select new
{
///I want to list the authorities belonging to
the customer
}).ToList();
}
There may be more than one authoritative definition of a customer. How can I do this in the LINQ query?
I spent WAY too much time doing your job, #IbrahimALPSOY:
Google Translating the turkish database screenshot,
Understanding which table holds what data using more Google Translate,
Understanding the expected result - which fields come from which tables,
Writing sample classes to represent the database,
Generating sample data for testing.
I wasted 30+ minutes before even starting to write a query. Next time I won't. Next time, you prepare your question such that, all other people could just copy your code and try queries right away.
This is the preparation:
class Authorisation
{
public int AuthorisationId; // yetkiliid
public int AccessId; // unvanid
public string AuthoriserName;
}
class Access
{
public int AccessId; // unvanid
public string AccessName;
}
class Customer
{
public int CustomerId; // musterid
public string CustomerName;
}
class Event
{
public int CustomerId;
public int AuthorisationId;
}
void Main()
{
var Customers = new[] {
new Customer() { CustomerId = 1, CustomerName = "Anne" },
new Customer() { CustomerId = 2, CustomerName = "Barbara" },
};
var Accesses = new[] {
new Access() { AccessId = 1, AccessName = "Read" },
new Access() { AccessId = 2, AccessName = "Write" },
};
var Authorisations = new[] {
new Authorisation() { AuthorisationId = 1, AuthoriserName = "The boss", AccessId = 1 }, // The boss can give read rights
new Authorisation() { AuthorisationId = 2, AuthoriserName = "The boss", AccessId = 2 }, // The boss can give write rights
new Authorisation() { AuthorisationId = 3, AuthoriserName = "A rookie", AccessId = 1 }, // A new employee can only give read rights
};
var Events = new[] {
new Event() { CustomerId = 1, AuthorisationId = 3 }, // A rookie let Anne read
new Event() { CustomerId = 1, AuthorisationId = 2 }, // While the boss let Anne write and scolded rookie
new Event() { CustomerId = 2, AuthorisationId = 1 }, // The boss thinks Barbara can't be trusted with write
};
}
I used this code instead of yours, because yours:
doesn't compile,
is illegible,
is badly formatted,
skips a table you've shown on your screenshot,
contains references to contexts only you have access to.
And here are the results:
Your query becomes feasible if you start with the table with non-unique keys:
from e in Events
join c in Customers on e.CustomerId equals c.CustomerId
join a in Authorisations on e.AuthorisationId equals a.AuthorisationId
join s in Accesses on a.AccessId equals s.AccessId
select new
{
e.CustomerId,
e.AuthorisationId,
c.CustomerName,
a.AuthoriserName,
s.AccessName
}
If this is not what you needed, modify my "preparation" to fit your question.

Filter Criteria Search using LINQ

I am 'newer' to LINQ queries have another one of those questions where I have something going but not sure if this is the most effective way to go about it. In my project, I am working in a real DB, but for a sake of simplicity, here I will condense it down to a simple list of employees:
var employees = new List<Employee>
{
new Employee { Id = 0, firstName = "James", LastName = "Bond", Manager = "M", StartDate = DateTime.Now },
new Employee { Id = 1, firstName = "Eric", LastName = "Bond", Manager = "M", StartDate = DateTime.Now },
new Employee { Id = 2, firstName = "Sue", LastName = "Milton", Manager = "Q", StartDate = DateTime.Now },
new Employee { Id = 3, firstName = "Olivia", LastName = "Milton", Manager = "M", StartDate = DateTime.Now },
new Employee { Id = 4, firstName = "Alice", LastName = "Raymond", Manager = "M", StartDate = DateTime.Now },
new Employee { Id = 5, firstName = "James", LastName = "Skywalker", Manager = "M", StartDate = DateTime.Now },
new Employee { Id = 6, firstName = "Luke", LastName = "Skywalker", Manager = "M", StartDate = DateTime.Now },
};
I have to search in this list based on given criteria.. where criteria is combination of various fields with OR and AND operations with in the fields for example get me all employees where:
firstName = "James" OR "eric" AND manager = "Q"
lastname = "bond" OR "Martha"
firstName = "James" AND Lastname = "Bond"
and so on...
This is going to be a web API call and I have to do this in one method. The other challenge is that each search parameter is 'optional" i.e , they can pass me a list of firstnames and a manager name and ignore the last names parameters etc. So here is what I started coded:
public IList<Employee> GetFilteredEmployees(IList<String> firstnames = null,
IList<String> lastnames = null,
IList<String> managers = null)
{
if (firstnames != null && firstnames.Any())
{
foreach (var fn in firstnames)
{
employeeByFn = employees.Where(emp => emp.firstName == fn).ToList<Employee>();
}
}
if (lastnames != null && lastnames.Any())
{
foreach (var ln in lastnames)
{
employeeByLn = employees.Where(emp => emp.LastName == ln).ToList<Employee>();
}
}
..... // code ellided
}
As you can see, this is getting ugly even with a few search criteria parameters. In my real project, I have up to 16 of those. Also at the end of all these sub-queries, I have to merge my results into one employee list and return that keeping in mind that any of the sub-query result may be null.
I am sure this is not a unique problem and I see similar questions asked before but not exactly the same problem. What would be elegant way of doing this that is also easy to maintain .i.e if they decide to add more search criteria later (say by start Date), I want to be able to easily modify my method to handle that.
Thanks a bunch for looking.
You can keep on adding Where() conditions on the same result instead of creating many partial results.
public IList<Employee> GetFilteredEmployees(IList<String> firstnames = null,
IList<String> lastnames = null,
IList<String> managers = null)
{
IQueryable<Employee> result = employees;
if (firstnames != null)
result = result.Where(emp => firstnames.Contains(emp.firstName));
if (lastnames != null)
result = result.Where(emp => lastnames.Contains(emp.LastName));
if (managers != null)
result = result.Where(emp => managers.Contains(emp.Manager));
... // code ellided
return result.ToList();
}

Concatenate Int to String in LINQ Query

I have the following LINQ query.
var providers = from c in Repository.Query<Company>()
where !c.IsDeleted
select new { c.Description, Id = "C" + c.Id };
I'm trying to concatenate the ID to "C". So, for example, if c.Id is 35 then the result should be "C35".
This obviously doesn't work because you can't add an integer (c.Id) to a string. I could easily resolve this in C# using string.Format() or converting the type. But how can I do this in LINQ?
Try using SqlFunctions.StringConvert Method:
var xd = (from c in Repository.Query<Company>()
where !c.IsDeleted
select new { c.Description, Id = "C" + SqlFunctions.StringConvert((double)c.Id).Trim()});
When you need functionality of .NET only in preparing the result (as opposed to, say, filtering, which should be done on RDBMS side to avoid bringing too much data in memory) the common trick is to complete the conversion in memory using the AsEnumerable method:
var providers = Repository.Query<Company>()
.Where(c => !c.IsDeleted)
.Select(c => new { c.Description, c.Id }) // <<== Prepare raw data
.AsEnumerable() // <<== From this point it's LINQ to Object
.Select(c => new { c.Description, Id = "C"+c.Id }); // <<== Construct end result
The code that you have written will work fine. Here is a mock up of the same code and it outputs the Id's
class Company
{
public string Description { get; set; }
public int Id { get; set; }
public bool IsDeleted { get; set; }
}
static void Main()
{
//setup
var list = new List<Company>();
list.Add(new Company
{
Description = "Test",
Id = 35,
IsDeleted = false
});
list.Add(new Company
{
Description = "Test",
Id = 52,
IsDeleted = false
});
list.Add(new Company
{
Description = "Test",
Id = 75,
IsDeleted = true
});
/* code you are looking for */
var providers = from c in list
where !c.IsDeleted
select new { c.Description, Id = "C" + c.Id };
foreach (var provider in providers)
{
Console.WriteLine(provider.Id);
}
Console.ReadKey();
}
What about string format
var providers = from c in Repository.Query<Company>()
where !c.IsDeleted
select new { c.Description, Id = "C" + c.Id.ToString() };

Categories

Resources