check if object already exists in Dictionary<string ,complexObject> - c#

I have a dictionary :
public Dictionary<string, ComplexType> ListUserPerFloor { get; set; }
public class ComplexType
{
public string Id { get; set; }
public decimal Duration { get; set; }
public ComplexType(string id, decimal duration)
{
IdUser = id;
Duration = duration;
}
}
public void GetUserPerFloor(TotalTimeViewModel model)
{
var users = getUsers();
foreach (var user in users)
{
if (!model.ListUserPerFloor.Any(dic => dic.Key.Contains(user.Id)))
{
var sum = user.Duration.GetValueOrDefault(0m);
model.ListUserPerFloor.Add(user.Floor.ToString(), new ComplexType(user.Id, sum));
}
else
{
var sum = sc.Duration.GetValueOrDefault(0m);
model.ListUserPerFloor[user.Floor.ToString()].Duration = model.ListUserPerFloor[user.Floor.ToString()].Duration + sum;
}
}
}
How can I check if there is a user exit in that complexe object, if user not exit , we add , else we update that object...
( the problem here : if (!model.ListTimeUserPerFloor.Any(dic => dic.Key.Contains(user.Id)))
i want to check if user.Id exit in the Dictionary )
example
(Floor :00,{0001,15000})
(Floor :00,{0002,16000})
...
thanks in advance.

You cannot have multiple objects with the same key in the dictionary and the dictionary Dictionary<string, ComplexType> seems incorrect for string total duration.
Generally for
How can I check if there is a user exit in that complexe object, if user not exit , we add , else we update that object...
you can use ContainsKey:
// ContainsKey can be used to test keys before inserting
// them.
if (!openWith.ContainsKey("ht"))
{
openWith.Add("ht", "hypertrm.exe");
Console.WriteLine("Value added for key = \"ht\": {0}",
openWith["ht"]);
}
or TryGetValue:
// When a program often has to try keys that turn out not to
// be in the dictionary, TryGetValue can be a more efficient
// way to retrieve values.
string value = "";
if (openWith.TryGetValue("tif", out value))
{
Console.WriteLine("For key = \"tif\", value = {0}.", value);
}
else
{
Console.WriteLine("Key = \"tif\" is not found.");
}
For your specific task I think you either want to store users per floor or only the sum of durations. Here is an example of what I mean:
var usersPerFloor = new Dictionary<int, List<ComplexType>>();
var users=getUsers();
foreach (var user in users)
{
if (!usersPerFloor.ContainsKey(user.Floor))
{
var l = new List<ComplexType>() { new ComplexType(user.Id, user.Duration)};
dictionary.Add(user.Floor, l);
}
else
{
var floorUsers = usersPerFloor[user.Floor];
var floorUser = floorUsers.FirstOrDefault(u => u.Id == user.Id);
if(floorUser == null)
{
floorUsers.Add( new ComplexType(user.Id, user.Duration));
}
else
{
floorUser.Duration += user.Duration;
}
}

I think you should use ListUserPerFloor.Values to iterate though them, e.g.
if (!model.ListUserPerFloor.Values.Any(v => v.IdUser.Equals(user.id)))
{
...
}

A little hard to say without knowing how TotalTimeViewModel model is defined, but try something similar to this:
foreach (var user in users)
{
if (model.ListUserPerFloor.GetType().GetProperties()
.Select(x => x.GetValue(model.ListUserPerFloor))
.Any(value => value.Contains(user.Id)))
{
// logic
}
else
{
// logic
}
}
You will likely need to modify this based on your implementation.

Related

Update the property of list items without "loop" and "if"

I am updating the property of the list items.
class Response
{
public string Name { get; set; }
public int Order { get; set; }
}
Here I want to update the Order of a List<Response> variable. As of now, I am looping through each item of the list and updating it.
List<Response> data = FromDb();
foreach (var item in data)
{
if(item.Name.Equals("A"))
{
item.Order=1;
}
if(item.Name.Equals("B"))
{
item.Order=2;
}
//Like this I have arround 20 conditions
}
The above code is working fine, but the problem is the Cognitive Complexity of the method is more than the allowed.
I tried something like below
data.FirstOrDefault(x => x..Equals("A")).Order = 1;
data.FirstOrDefault(x => x..Equals("B")).Order = 2;
//and more ...
In this code also null check is not in place, So if the searching string is not present in the list then again it will break.
If I add null check condition then again the complexity getting higher.
So here I want without any for loop or if, If I can update the Order of the list by using linq/lamda or anything else.
I don't know how you measure Cognitive Complexity and how much of it is allowed to be pushed out into other functions, but something like this makes the ordering quite declarative?
[Fact]
public void TestIt()
{
var data = FromDb().Select(SetOrder(
("A", 1),
("B", 2)
));
}
static Func<Response, Response> SetOrder(params (string Name, int Order)[] orders)
{
var orderByKey = orders.ToDictionary(x => x.Name);
return response =>
{
if (orderByKey.TryGetValue(response.Name, out var result))
response.Order = result.Order;
return response;
};
}
Addendum in response to comment:
In order to have a default value for unmatched names, the SetOrder could be changed to this:
static Func<Response, Response> SetOrder(params (string Name, int Order)[] orders)
{
var orderByKey = orders.ToDictionary(x => x.Name);
return response =>
{
response.Order =
orderByKey.TryGetValue(response.Name, out var result)
? result.Order
: int.MaxValue;
return response;
};
}

Finding if a property inside a list inside a list doesnt contain a specific string

I have a huge product database im working with threw a api
The API returns a Product those Products have MetaData which are stuff like minQuantities Maxes ect
Now I noticed some products dont have "variation_maximum_allowed_quantity" or " maximum_allowed_quantity"
So for every Product I have to look threw That Products meta data to so something like
foreach (var item in VarProduct)
{
foreach (var metaItem in item.meta_data)
{
}
}
The issue is meta_data have Key's Id's and value's I need to check against every key inside of every meta_data inside of every Product if that makes sense? Im wrecking my brain atm, Anyone know how I can check against all of those to see if those 2 Strings are missing? I need to be able to do something like
if(mdDoesntcontain)
{
//This means stock is Unlimiteed so need to set a value for stock
item.stockquantity = 9999999; (or int.maxValue())
}
Little tricky Posting this as a Block but:
-item.meta_data count = w.e
-[0]
-base
-id = 101231
-key = "min_max_rules"
-value = "no"
-[1]
-base
-id = 123131
-key = "variation_minimum_allowed_quantity"
-value = ""(this means 1 usually)
public class MetaData
{
private object preValue;
[DataMember (EmitDefaultValue = false)]
public int? id {
get;
set;
}
[DataMember (EmitDefaultValue = false)]
public string key {
get;
set;
}
[DataMember (EmitDefaultValue = false)]
public object value {
get;
set;
}
foreach (var item in VarProduct)
{
hasQuantity = false;
foreach (var metaItem in item.meta_data)
{
if (metaItem.id == "variation_maximum_allowed_quantity" || metaItem.id == "maximum_allowed_quantity") {
hasQuantity = true;
break;
}
}
if (!hasQuantity) {
item.stockQuantity = int.MaxValue;
}
}
Would this be the solution you're looking for?
foreach (var item in VarProduct)
{
var hasQuantity = false;
foreach (var metaItem in item.meta_data)
{
if (metaItem.key == "variation_maximum_allowed_quantity" || metaItem.key == "maximum_allowed_quantity") {
item.stockQuantity = metaItem.value == "" ? 1 : int.Parse(metaItem.value);
hasQuantity = true;
break;
}
}
if(!hasQuantity) {
item.stockQuantity = int.MaxValue;
}
}
You could use linq Any operation:
foreach (var item in VarProduct)
{
if (!item.meta_data.Any(m => m.key.Equals("variation_maximum_allowed_quantity")
|| m.key.Equals("maximum_allowed_quantity")))
{
item.stockQuantity = int.MaxValue;
}
}

rearrange a list of objects by type field in C#

I have an incoming list of alerts and I use a MapFunction as:
private static BPAlerts MapToAlerts(List<IntakeAlert> intakeAlerts)
{
// Make sure that there are alerts
if (intakeAlerts.IsNullOrEmpty()) return new BPAlerts { AllAlerts = new List<BPAlert>(), OverviewAlerts = new List<BPAlert>() };
// All Alerts
var alerts = new BPAlerts
{
AllAlerts = intakeAlerts.Select(
alert => new BPAlert
{
AlertTypeId = alert.AlertTypeId ?? 8100,
IsOverview = alert.IsOverviewAlert.GetValueOrDefault(),
Text = alert.AlertText,
Title = alert.AlertTitle,
Type = alert.AlertTypeId == 8106 ? "warning" : "report",
Severity = alert.AlertSeverity.GetValueOrDefault(),
Position = alert.Position.GetValueOrDefault()
}).OrderBy(a => a.Position).ToList()
};
// Alerts displayed on the overview page
alerts.OverviewAlerts =
alerts.AllAlerts
.ToList()
.Where(a => a.IsOverview && !string.IsNullOrEmpty(a.Title))
.Take(3)
.ToList();
return alerts;
}
the BPAlerts type contains list of two type:
public class BPAlerts
{
public List<BPAlert> AllAlerts { get; set; }
public List<BPAlert> OverviewAlerts { get; set; }
}
And the BPAlert type is defined as:
public class BPAlert
{
public short AlertTypeId { get; set; }
public string Type { get; set; }
public int Severity { get; set; }
public bool IsOverview { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public int Position { get; set; }
public string Id { get; internal set; } = Guid.NewGuid().ToString();
}
I want to achieve a task in which the MaptoAlerts function returns a alerts object with overviewalerts which are sorted based on the type of BPAlert. To be more clear in the following order if present:
Confirmed Out of Business - 8106 \n
Bankruptcy - 8105 \n
Lack of Licensing - 8111 \n
Investigations - 8109 \n
Government Actions - 8103 \n
Pattern of Complaints - 8104 \n
Customer Reviews - 8112 \n
Accreditation - 8110 \n
Misuse of BBB Name - 8101 \n
Advisory - 8107 \n
Advertising Review – 8102 \n
Solution #1 Order values array
I would just define the order of those ids in some kind of collection, can be an array:
var orderArray = new int[]
{
8106, // Confirmed Out of Busine
8105, // Bankruptcy
8111, // Lack of Licensing
8109, // Investigations
8103, // Government Actions
8104, // Pattern of Complaints
8112, // Customer Reviews
8110, // Accreditation
8101, // Misuse of BBB Name
8107, // Advisory
8102, // Advertising Review
};
Then iterate through array while incrementing order value. While looping check if order array contains actual type id which order value I'm trying to evaluate:
for (int orderValue = 0; orderValue < orderArray.Length; orderValue++)
{
if (alertTypeId == orderArray[orderValue])
{
return orderValue;
}
}
If not found in the array, return highest value possible:
return int.MaxValue
Whole method would look like this and it would evaluate the order for alert type id:
public int GetAlertTypeIdOrder(short alertTypeId)
{
var orderArray = new int[]
{
8106, // Confirmed Out of Busine
8105, // Bankruptcy
8111, // Lack of Licensing
8109, // Investigations
8103, // Government Actions
8104, // Pattern of Complaints
8112, // Customer Reviews
8110, // Accreditation
8101, // Misuse of BBB Name
8107, // Advisory
8102, // Advertising Review
};
for (int orderValue = 0; orderValue < orderArray.Length; orderValue++)
{
if (alertTypeId == orderArray[orderValue])
{
return orderValue;
}
}
return int.MaxValue;
}
Usage:
var sortedAlerts = alerts
.AllAlerts
.OrderByDescending(a => GetAlertTypeIdOrder(a.AlertTypeId))
.ToList();
It also works in a descending way.
Solution #2 Order values dictionary
You could achieve better performance by reducing the redundancy - repeated creation of array storing order values. Better idea would be to store the order rules in a dictionary. I know that code below creates an array too, but the concept is that it would be called once to get the dictionary which would be then passed over.
public Dictionary<int, int> GetOrderRules()
{
var alertTypeIds = new int[]
{
8106, // Confirmed Out of Busine
8105, // Bankruptcy
8111, // Lack of Licensing
8109, // Investigations
8103, // Government Actions
8104, // Pattern of Complaints
8112, // Customer Reviews
8110, // Accreditation
8101, // Misuse of BBB Name
8107, // Advisory
8102, // Advertising Review
};
var orderRules = new Dictionary<int, int>();
for (int orderValue = 0; orderValue < alertTypeIds.Length; orderValue++)
{
orderRules.Add(alertTypeIds[orderValue], orderValue);
}
return orderRules;
}
So the GetAlertIdOrder() method would look different, but still keeping the idea from previous solution:
public int GetAlertIdOrder(short alertTypeId, IDictionary<int, int> orderRules)
{
if (orderRules.TryGetValue(alertTypeId, out int orderValue))
{
return orderValue;
}
else
{
return int.MaxValue;
}
}
Usage:
var orderRules = GetOrderRules();
var sortedAlerts = alerts
.AllAlerts
.OrderBy(a => GetAlertIdOrder(a.AlertTypeId, orderRules))
.ToList();
(a) I wouldn't mix sorting with the mapper. let the mapper just do its thing. (this is separation of concerns ) .. aka, no ordering/sorting. IMHO, you'll always end up with way too much voodoo in the mapper that is hard to understand. You're already on this path with the above code.
(b) if "OverviewAlerts" is a subset of AllAlerts (aka, AllAlerts is the superset), then hydrate AllAlerts, and create a read-only "get" property where you filter AllAlerts to your subset by its rules. optionally, consider a AllAlertsSorted get property. this way, you allow your consumers to choose if they want raw or sorted...since there is a cost with sorting.
public class BPAlerts
{
public List<BPAlert> AllAlerts { get; set; }
public List<BPAlert> OverviewAlerts {
get
{
return null == this.AllAlerts ? null : this.AllAlerts.Where (do you filtering and maybe sorting here ) ;
}
}
}
public List<BPAlert> AllAlertsSorted{
get
{
return null == this.AllAlerts ? null : this.AllAlerts.Sort(do you filtering and maybe sorting here ) ;
}
}
}
if you do the read-only properties, then you have more simple linq operations like
OrderBy(x => x.PropertyAbc).ThenByDescending(x => x.PropertyDef);
99% of my mapping code looks like this. I don't even throw an error if you give null input, i just return null.
public static class MyObjectMapper {
public static ICollection < MyOtherObject > ConvertToMyOtherObject(ICollection <MyObjectMapper> inputItems) {
ICollection <MyOtherObject> returnItems = null;
if (null != inputItems) {
returnItems = new List <MyOtherObject> ();
foreach(MyObjectMapper inputItem in inputItems) {
MyOtherObject moo = new MyOtherObject();
/* map here */
returnItems.Add(moo);
}
}
return returnItems;
}
}

C# custom add in a List

I have the following list of strings :
var files = new List<string> {"file0","file1","file2","file3" };
I would like to be able to add new files to this list, but if the inserted file is present in the list, I would like to insert custom value that will respect the following format $"{StringToBeInserted}"("{SomeCounter}
For instance : try to add "file0" and "file0" is already I would like to insert "file0(1)". If I try again to add "file0" ... I would like to insert with "file0(2)" and so on ... Also, I would like to provide a consistency, for instance if I delete "file0(1)" ... and try to add again "item0" ... I expect that "item0(1)" to be added. Can someone help me with a generic algorithm ?
I would use a HashSet<string> in this case:
var files = new HashSet<string> { "file0", "file1", "file2", "file3" };
string originalFile = "file0";
string file = originalFile;
int counter = 0;
while (!files.Add(file))
{
file = $"{originalFile}({++counter})";
}
If you have to use a list and the result should also be one, you can still use my set approach. Just initialize it with your list and the result list you'll get with files.ToList().
Well, you should create your own custom class for it, using the data structure you described and a simple class that includes a counter and an output method.
void Main()
{
var items = new ItemCountList();
items.AddItem("item0");
items.AddItem("item1");
items.AddItem("item2");
items.AddItem("item0");
items.ShowItems();
}
public class ItemCountList {
private List<SimpleItem> itemList;
public ItemCountList() {
itemList = new List<SimpleItem>();
}
public void DeleteItem(string value) {
var item = itemList.FirstOrDefault(b => b.Value == value);
if (item != null) {
item.Count--;
if (item.Count == 0)
itemList.Remove(item);
}
}
public void AddItem(string value) {
var item = itemList.FirstOrDefault(b => b.Value == value);
if (item != null)
item.Count++;
else
itemList.Add(new SimpleItem {
Value = value,
Count = 1
});
}
public void ShowItems() {
foreach (var a in itemList) {
Console.WriteLine(a.Value + "(" + a.Count + ")");
}
}
}
public class SimpleItem {
public int Count {get; set;}
public string Value {get; set;}
}

Avoid inserting duplicates in sql table through Entity Framework with multiple workers

I have a SQL table called Domain which contains the following columns:
I have multiple agents (workers) inserting rows into the database at the same time and I want to avoid inserting duplicate domains. The Id is the primary key and I not willing to change it.
Meanwhile Im checking first if the domain exist by this:
public async Task<List<DomainApiModel>> GetListOfExistingDomainsAsync(List<string> domains)
{
using (eTrafficBacklinks_V2Entities EMME_Context = new eTrafficBacklinks_V2Entities())
{
var rec = await EMME_Context.Domains.Where(p => domains.Contains(p.DomainName)).ToListAsync();
return rec.Select(p => new DomainApiModel(p)).ToList();
}
}
Then I filter the ones already existent and, finally, I insert the non existing by the following code:
public int Create(List<DomainApiModel> domainApiList, out List<DomainApiModel> domainApiListWithId, int chunkSizeLimit = 500)
{
using (eTrafficBacklinks_V2Entities EMME_Context = new eTrafficBacklinks_V2Entities())
{
EMME_Context.Configuration.AutoDetectChangesEnabled = false;
EMME_Context.Configuration.ValidateOnSaveEnabled = false;
int totalChanges = 0;
var listOfLists = domainApiList.ChunkSplit(chunkSizeLimit).ToList();
var listOfDomainData = new List<Domain>();
foreach (var list in listOfLists)
{
foreach (var apiModel in list)
{
var objectData = apiModel.GetDataObject();
EMME_Context.Domains.Add(objectData);
listOfDomainData.Add(objectData);
}
totalChanges += EMME_Context.SaveChanges();
}
domainApiListWithId = listOfDomainData.Select(d => new DomainApiModel(d)).ToList();
return totalChanges;
}
}
The problem is that in the meantime between checking if the domain exists and creating, another agent can insert the same domain and I am having duplicates in my table.
Anyone has any light of how to solve this problem?
Obs: I have the same problem with a table called page with the "url column" which is nvarchar950 type, so creating just a unique index is not a solution...
This problem can be easily solved adding a unique index to the table. Any attempt to add a duplicate value will throw an exception.
CREATE UNIQUE INDEX UX_DOMAIN_NAME
ON DOMAIN (DOMAIN_NAME)
Beware than it will require you to add each new value independently or the whole transaction will fail, even for those non duplicated values.
foreach (var list in listOfLists)
{
foreach (var apiModel in list)
{
var objectData = apiModel.GetDataObject();
EMME_Context.Domains.Add(objectData);
listOfDomainData.Add(objectData);
try{
totalChanges += EMME_Context.SaveChanges();
}
catch(SqlException se){
if(se.Number != 2601) // Unique key violation
{
// Handle other errors
}
}
}
}
Add an additional Index on all columns that need to be unique. Columns that are to big to be indexed can be indexed by a computed hash.
Here a concept for creating many entities and how to track the exceptions:
class MyService
{
public async Task<OperationResult<string, SomeEntity>> CreateManyAsync( IList<string> data, int chunkSize )
{
var succeded = new List<SomeEntity>( );
var failed = new List<FailedOperation<string>>( );
foreach ( var chunk in data.Select( ( dataItem, index ) => new { data = dataItem, chunk = index % chunkSize } ).GroupBy( c => c.chunk, c => c.data ) )
{
try
{
succeded.AddRange( await InternalCreateManyAsync( chunk ) );
continue;
}
catch ( Exception )
{
// we just eat this exception
}
foreach ( var singleItem in chunk )
{
try
{
succeded.Add( await InternalCreateSingleAsync( singleItem ) );
}
catch ( Exception ex )
{
failed.Add( new FailedOperation<string>( singleItem, ex ) );
}
}
}
return new OperationResult<string, SomeEntity> {
Succeded = succeded,
Failed = failed,
};
}
private async Task<IList<SomeEntity>> InternalCreateManyAsync( IEnumerable<string> data )
{
var result = new List<SomeEntity>( );
using ( var db = new MyCOntext( ) )
{
foreach ( var item in data )
{
result.Add( AddSingleToContext( item, db ) );
}
await db.SaveChangesAsync( );
}
return result;
}
private async Task<SomeEntity> InternalCreateSingleAsync( string data )
{
using ( var db = new MyContext( ) )
{
var e = AddSingleToContext( data, db );
await db.SaveChangesAsync( );
return e;
}
}
private SomeEntity AddSingleToContext( string data, MyContext context )
{
var entity = new SomeEntity { Data = data, };
context.SomeEntities.Add( entity );
return entity;
}
}
some utility classes
class SomeEntity
{
public int Id { get; set; }
public string Data { get; set; }
}
class FailedOperation<T>
{
public FailedOperation( T data, Exception error )
{
Data = data;
Error = error;
}
public T Data { get; }
public Exception Error { get; }
}
class OperationResult<TSource, TResult>
{
public IList<TResult> Succeded { get; set; }
public IList<FailedOperation<TSource>> Failed { get; set; }
}

Categories