I have a method that allows me to create an agent list.
This list of agents contains "Nom", "Prenom", "Matricule", "Mail".
I use.distinct() to sort them, but this only takes into account "Nom", "Prenom", "Matricule". This does not check if the emails are different. How do I proceed?
The creation of my agent list:
private ObservableCollection<AgentMailModel> _Agents;
public ObservableCollection<AgentMailModel> Agents
{
get
{
return _Agents;
}
set
{
if (value != _Agents)
{
_Agents = value;
RaisePropertyChanged("Agents");
}
}
}
foreach (Destinataire dst in (await _dataService.GetDestinatairesAsync()))
_TmpAgents.Add(new AgentMailModel() { Matricule = dst.Matricule, Nom = dst.Nom, Prenom = dst.Prenom, Mail = dst.Mail });
foreach (AgentModel ag in (await _dataService.GetAgentsContratsAsync()))
_TmpAgents.Add(new AgentMailModel() { Matricule = ag.Matricule, Nom = ag.Nom, Prenom = ag.Prenom, Mail = ag.Mail });
Agents = new ObservableCollection<AgentMailModel(_TmpAgents.Distinct());
My list in WPF :
My database :
As you can see :
It displays "carré" (cause "Nom" is different, It also works with a different "Prenom" or "Matricule) and only one "carre" (without "é").
Distinct() doesn't work with my Mails. Any tips ?
You have two possibilities.
First one is to create a class of IEqualityComparer and implement a full comparison.
you can find an example here.
The second one is to convert the objects to JSON, and made of comparison of strings instead of objects.
Related
How to combine Id from the list I get from file /test.json and id from list ourOrders[i].id?
Or if there is another way?
private RegionModel FilterByOurOrders(RegionModel region, List<OurOrderModel> ourOrders, MarketSettings market, bool byOurOrders)
{
var result = new RegionModel
{
updatedTs = region.updatedTs,
orders = new List<OrderModel>(region.orders.Count)
};
var json = File.ReadAllText("/test.json");
var otherBotOrders = JsonSerializer.Deserialize<OrdersTimesModel>(json);
OtherBotOrders = new Dictionary<string, OrderTimesInfoModel>();
foreach (var otherBotOrder in otherBotOrders.OrdersTimesInfo)
{
//OtherBotOrders.Add(otherBotOrder.Id, otherBotOrder);
BotController.WriteLine($"{otherBotOrder.Id}"); //Output ID orders to the console works
}
foreach (var order in region.orders)
{
if (ConvertToDecimal(order.price) < 1 || !byOurOrders)
{
int i = 0;
var isOurOrder = false;
while (i < ourOrders.Count && !isOurOrder)
{
if (ourOrders[i].id.Equals(order.id, StringComparison.InvariantCultureIgnoreCase))
{
isOurOrder = true;
}
++i;
}
if (!isOurOrder)
{
result.orders.Add(order);
}
}
}
return result;
}
OrdersTimesModel Looks like that:
public class OrdersTimesModel
{
public List<OrderTimesInfoModel> OrdersTimesInfo { get; set; }
}
test.json:
{"OrdersTimesInfo":[{"Id":"1"},{"Id":"2"}]}
Added:
I'll try to clarify the question:
There are three lists with ID:
First (all orders): region.orders, as order.id
Second (our orders): ourOrders, as ourOrders[i].id in a while loop
Third (our orders 2): from the /test.json file, as an array {"Orders":[{"Id":"12345..."...},{"Id":"12345..." ...}...]}
There is a foreach in which there is a while, where the First (all orders) list and the Second (our orders) list are compared. If the id's match, then these are our orders: isOurOrder = true;
Accordingly, those orders that isOurOrder = false; will be added to the result: result.orders.Add(order)
I need:
So that if (ourOrders[i].id.Equals(order.id, StringComparison.InvariantCultureIgnoreCase)) would include more Id's from the Third (our orders 2) list.
Or any other way to do it?
You should be able to completely avoid writing loops if you use LINQ (there will be loops running in the background, but it's way easier to read)
You can access some documentation here: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/introduction-to-linq-queries
and you have some pretty cool extension methods for arrays: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable?view=net-6.0 (these are great to get your code easy to read)
Solution
unsing System.Linq;
private RegionModel FilterByOurOrders(RegionModel region, List<OurOrderModel> ourOrders, MarketSettings market, bool byOurOrders)
{
var result = new RegionModel
{
updatedTs = region.updatedTs,
orders = new List<OrderModel>(region.orders.Count)
};
var json = File.ReadAllText("/test.json");
var otherBotOrders = JsonSerializer.Deserialize<OrdersTimesModel>(json);
// This line should get you an array containing
// JUST the ids in the JSON file
var idsFromJsonFile = otherBotOrders.Select(x => x.Id);
// Here you'll get an array with the ids for your orders
var idsFromOurOrders = ourOrders.Select(x => x.id);
// Union will only take unique values,
// so you avoid repetition.
var mergedArrays = idsFromJsonFile.Union(idsFromOurOrders);
// Now we just need to query the region orders
// We'll get every element that has an id contained in the arrays we created earlier
var filteredRegionOrders = region.orders.Where(x => !mergedArrays.Contains(x.id));
result.orders.AddRange(filteredRegionOrders );
return result;
}
You can add conditions to any of those actions (like checking for order price or the boolean flag you get as a parameter), and of course you can do it without assigning so many variables, I did it that way just to make it easier to explain.
This is the constructor (it's fullpropped, but here I only show 1st line):
Public Class Player (string firstName, string lastName, string email)
Program:
Player player1 = new Player("Mike","Dalton", md#mail.com);
Player player2 = new Player("Mario","Davis", mdavis#mail.com);
Player player3 = new Player("Mia","Duncan", md#mail.com);
...
List<Player> players = new List<Player> { player1, player2, player3};
There are 3 players in the list. The real list is pretty long. There needs to be a check if all email-adresses are unique.
(As you see: email_player1 = email_player 3)
At the end, a simple message (console.writeline) should tell whether or not there are email_doubles or not.
Can you help me out?
You can try the following approach:
List<string> emailList = players.Select(x => x.Email).ToList();
if (emailList.Count > emailList.Distinct().Count())
{
Console.WriteLine("Duplicate email address found.");
}
If you want to display duplicate email addresses specifically then can also try the following approach:
var playersGroupByEmailList = players.GroupBy(x => x.Email).Select(g => new { Email = g.Key, Count = g.Count() });
playersGroupByEmailList = playersGroupByEmailList.Where(x => x.Count > 1).ToList();
if (playersGroupByEmailList.Any())
{
foreach (var playerGroupByEmail in playersGroupByEmailList)
{
Console.WriteLine($"{playerGroupByEmail.Email} is duplicate.");
}
}
try this...
this will give you list of all the duplicate Emails that exists in the list.
players.GroupBy(x => x.Email).SelectMany(g => g.Skip(1));
Select(...) will help you get the attribute of the elements in the list.
Distinct() will help you make the list contains unique elements from the list.
If you want to check all elements are unique, you could use:
bool isUnique = players.Select(x => x.Email).Distinct().Count() == players.Select(x => x.Email).Count();
With:
players.Distinct().Count(): count the unique elements.
players.Count(): count the list elements.
If they are same (isUnique = true), then the all the elements in list are unique.
If you consider the email address to be like a key of a database table in that it is unique and only want to store one of each key/e-mail then maybe List is not the best collection.
You can instead use HashSet which only accepts unique items.
See Microsoft documentation: https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1?redirectedfrom=MSDN&view=net-5.0
You declar it like this
HashSet<Player> players = new HashSet<Player>();
When you try to add it, then it returns true to false depending if it was accepted (is unique) or not.
Add to set:
if(players.Add(player))
Console.Writeline($"Player {player} was added OK");
else
Console.Writeline($"Player with email {player.Email} already exists in the collection!");
In order to check for uniqueness then you need to implement in IEquatable<T> your class
public class Player: IEquatable<Player>
{
public string Email { get; set; }
//Other properties, Constructor & methods etc.
//It is important to implement GetHashCode!
public override int GetHashCode()
{
return Email.GetHashCode();
}
public bool Equals(Player other)
{
return this.Email.Equals(other.Email);
}
}
As a basic way, you can do like below:
List<string> controlList = new List<string>();
foreach (var player in PlayersInfo)
{
if (controlList.Contains(player.Email))
{
Console.WriteLine("there is a duplicate with" + player.email);
}
else
{
Console.WriteLine(player.Email)
}
controlList.Add(player.Email);
}
i know it is not complicated but i struggle with it.
I have IList<Material> collection
public class Material
{
public string Number { get; set; }
public decimal? Value { get; set; }
}
materials = new List<Material>();
materials.Add(new Material { Number = 111 });
materials.Add(new Material { Number = 222 });
And i have DbSet<Material> collection
with columns Number and ValueColumn
I need to update IList<Material> Value property based on DbSet<Material> collection but with following conditions
Only one query request into database
The returned data from database has to be limited by Number identifier (do not load whole database table into memory)
I tried following (based on my previous question)
Working solution 1, but download whole table into memory (monitored in sql server profiler).
var result = (
from db_m in db.Material
join m in model.Materials
on db_m.Number.ToString() equals m.Number
select new
{
db_m.Number,
db_m.Value
}
).ToList();
model.Materials.ToList().ForEach(m => m.Value= result.SingleOrDefault(db_m => db_m.Number.ToString() == m.Number).Value);
Working solution 2, but it execute query for each item in the collection.
model.Materials.ToList().ForEach(m => m.Value= db.Material.FirstOrDefault(db_m => db_m.Number.ToString() == m.Number).Value);
Incompletely solution, where i tried to use contains method
// I am trying to get new filtered collection from database, which i will iterate after.
var result = db.Material
.Where(x=>
// here is the reasonable error: cannot convert int into Material class, but i do not know how to solve this.
model.Materials.Contains(x.Number)
)
.Select(material => new Material { Number = material.Number.ToString(), Value = material.Value});
Any idea ? For me it is much easier to execute stored procedure with comma separated id values as a parameter and get the data directly, but i want to master linq too.
I'd do something like this without trying to get too cute :
var numbersToFilterby = model.Materials.Select(m => m.Number).ToArray();
...
var result = from db_m in db.Material where numbersToFilterBy.Contains(db_m.Number) select new { ... }
I'm doing a wpf App and i've got a bit of an issue i would like to ask you about.
I'm querying a database on the window level and i pass the result of the query to a method in my object like this :
Window level code :
payrollEmailManager.SetListOfSalariesToEmailTo(
from Record in SqlInfo.SqlTable.T_SALs
where Record.EtatPaie == 3
select new {
Matricule = Record.MatriculeSalarie,
Nom = Record.Nom,
Prenom = Record.Prenom,
Email = Record.EMail });
This is my Method Definition :
public void SetListOfSalariesToEmailTo(object _ListOfSalaryToRecieveMail)
{
ListOfSalary = _ListOfSalaryToRecieveMail;
}
Where ListOfSalary is also of type object.
Now here is the issue for me, I have another method where I want to go trough each record of listofsalary and get the information I selected in query like Matricule or Email, something like this :
public void SendEmail()
{
foreach(var Salary in (dynamic)ListOfSalary)
{
Mail.To.Add(Salary.????
}
}
I can't reference the Nom column or the Email column any advice ??
If you consider your following query:
var query = from Record in SqlInfo.SqlTable.T_SALs
where Record.EtatPaie == 3
select new {
Matricule = Record.MatriculeSalarie,
Nom = Record.Nom,
Prenom = Record.Prenom,
Email = Record.EMail
};
After running this line the query is not yet executed to database. Only when you materialize it (using functions like ToList()/ToArray()/FirstOrDefault etc.) it is actually being executed in the database and information is returned.
Therefore if you just do SomeFunction(query); it does not execute the query and you can store it for later execution.
However you do need to change your code a bit:
The function should not get object but IQueryable<T>
public void SetListOfSalariesToEmailTo(IQueryable<T> query)
As you want to store the query you need to later on know the type of each item. To do so do not use an anonymous object (new { }) in the select. Use instead a custom object or use c# 7.0 named tuples and then the function will look like:
var query = from Record in SqlInfo.SqlTable.T_SALs
where Record.EtatPaie == 3
select new SomeType {
Matricule = Record.MatriculeSalarie,
Nom = Record.Nom,
Prenom = Record.Prenom,
Email = Record.EMail
};
public void SetListOfSalariesToEmailTo(IQueryable<SomeType> query)
{
ListOfSalary = query;
}
You can still use object and dynamic as you did, and just access the properties, but you will not have the intellisense showing you the properties and options, as it does not know the concrete type.
In the following code that returns a list:
public List<Customer> GeAllCust()
{
var results = db.Customers
.Select(x => new { x.CustName, x.CustEmail, x.CustAddress, x.CustContactNo })
.ToList()
return results;
}
I get an error reporting that C# can't convert the list:
Error: Cannot implicitly convert type System.Collections.Generic.List<AnonymousType#1> to System.Collections.Generic.List<WebApplication2.Customer>
Why is that?
Here's a screenshot showing some additional information that Visual Studio provides in a tooltip for the error:
Is it right way to return some columns instead of whole table....?
public object GeAllCust()
{
var results = db.Customers.Select(x => new { x.CustName, x.CustEmail, x.CustAddress, x.CustContactNo }).ToList();
return results;
}
When you look the code:
x => new { ... }
This creates a new anonymous type. If you don't need to pull back only a particular set of columns, you can just do the following:
return db.Customers.ToList();
This assumes that Customers is an IEnumerable<Customer>, which should match up with what you are trying to return.
Edit
You have noted that you only want to return a certain subset of columns. If you want any sort of compiler help when coding this, you need to make a custom class to hold the values:
public class CustomerMinInfo
{
public string Name { get; set; }
public string Email { get; set; }
public string Address { get; set; }
public int? ContactNumber { get; set; }
}
Then change your function to the following:
public List<CustomerMinInfo> GetAllCust()
{
var results = db.Customers.Select(x => new CustomerMinInfo()
{
Name = x.CustName,
Email = x.Email,
Address = x.Address,
ContactNumber = x.CustContactNo
})
.ToList();
return results;
}
This will work, however, you will lose all relationship to the database context. This means if you update the returned values, it will not stick it back into the database.
Also, just to repeat my comment, returning more columns (with the exception of byte arrays) does not necessarily mean longer execution time. Returning a lot of rows means more execution time. Your function is returning every single customer in the database, which when your system grows, will start to hang your program, even with the reduced amount of columns.
You are selecting to an anonymous type, which is not a Customer.
If you want to do (sort of) this, you can write it like this:
return db.Customers.Select(x => new Customer { Name = x.CustName, Email = x.CustEmail, Address = x.CustAddress, ContactNo = x.ContactNo }).ToList();
This assumes the properties on your Customer object are what I called them.
** EDIT ** Per your comment,
If you want to return a subset of the table, you can do one of two things:
Return the translated form of Customer as I specified above, or:
Create a new class for your business layer that only has only those four fields, and change your method to return a List<ShrunkenCustomer> (assuming ShunkenCustomer is the name that you choose for your new class.)
GetAllCust() is supposed to return a List of Customer, Select New will create a list of Anonymous Types, you need to return a list of Customer from your query.
try:
var results = db.Customers.Select( new Customer{CustName = x.CustName}).ToList(); //include other fields
I guess Customer is a class you have defined yourself?
The my suggestion would be to do something like the following:
var results = db.Customers.Select(x => new Customer(x.Custname, x.CustEmail, x.CustAddress, x.CustContactNo)).ToList();
The reason is that you are trying to return a list of Customer but the results from your link is an anonymous class containing those four values.
This would of course require that you have a constructor that takes those four values.
Basically whatever u got in var type, loop on that and store it in list<> object then loop and achieve ur target.Here I m posting code for Master details.
List obj = new List();
var orderlist = (from a in db.Order_Master
join b in db.UserAccounts on a.User_Id equals b.Id into abc
from b in abc.DefaultIfEmpty()
select new
{
Order_Id = a.Order_Id,
User_Name = b.FirstName,
Order_Date = a.Order_Date,
Tot_Qty = a.Tot_Qty,
Tot_Price = a.Tot_Price,
Order_Status = a.Order_Status,
Payment_Mode = a.Payment_Mode,
Address_Id = a.Address_Id
});
List<MasterOrder> ob = new List<MasterOrder>();
foreach (var item in orderlist)
{
MasterOrder clr = new MasterOrder();
clr.Order_Id = item.Order_Id;
clr.User_Name = item.User_Name;
clr.Order_Date = item.Order_Date;
clr.Tot_Qty = item.Tot_Qty;
clr.Tot_Price = item.Tot_Price;
clr.Order_Status = item.Order_Status;
clr.Payment_Mode = item.Payment_Mode;
clr.Address_Id = item.Address_Id;
ob.Add(clr);
}
using(ecom_storeEntities en=new ecom_storeEntities())
{
var Masterlist = en.Order_Master.OrderByDescending(a => a.Order_Id).ToList();
foreach (var i in ob)
{
var Child = en.Order_Child.Where(a => a.Order_Id==i.Order_Id).ToList();
obj.Add(new OrderMasterChild
{
Master = i,
Childs = Child
});
}
}