I am trying to update a nested list in C# which looks like this
List<Users>
- UserType
- List<UserComponents>
- - UserComponentKey
- - Count
Here's a written example:
List of users:
UserType = 1
UserComponents
- UserComponentKey = XYZ
- Count = 3
UserType = 2
UserComponents
- UserComponentKey = XYZ
- Count = 7
I need to update UserComponentKey XYZ for UserType 2 only, currently my updates are broken and updates XYZ for all user types. Here is my current methods which do not work as they update the UserComponent count value for ALL usertypes which contain the specified component key, and not the specific usertype I am targeting.
CLASSES:
public class Users
{
public string UserType { get; set; }
public List<UserComponent> UserComponents { get; set; }
}
public class UserComponent
{
public string UserComponentKey { get; set; }
public int Count { get; set; }
}
METHOD 1:
Users.Where(us => us.UserType == "2")
.First().UserComponents
.Where(uc => uc.UserComponentKey == "XYZ")
.First().Count = value;
METHOD 2:
if(users.UserType == "2")
{
foreach(var component in users.UserComponents)
{
switch(component.UserComponentKey)
{
case "XYZ":
component.Count = value;
break;
}
}
}
CODE GENERATING LIST (similar to):
List UserComponents = new List();
if (Item.UserAddOn != null)
{
for (var i = 0; i < Item.UserAddOn.First().Count; i++)
{
UserComponents.Add(new UserComponent
{
UserComponentKey = Item.UserAddOn[i].ComponentKey,
Count = 0
});
}
}
if (Item.User != null)
{
for (var i = 0; i < Item.User.First().Count; i++)
{
Users.Add(new User()
{
UserType = Item.User[i].ComponentKey,
Count = 0,
UsersComponents = UserComponents
});
}
}
I have stripped out actual values etc, but hopefully someone can point me in the right direction here.
Thanks!
I'm missing information to write a snippet you can use so I will simply explain it. An object variable is in reality a reference (a pointer, if you are familiar with C++/C) to the location where the object reside. When you add an object to a list, you add it's location. If you add this object to multiple list, you give the same location and therefor, editing one of them will edit all of them.
var uc1 = new UserComponent { Count = 1 };
var uc2 = new UserComponent { Count = 2 };
var uc3 = new UserComponent { Count = 2 };
var u1 = new User();
var u2 = new User();
u1.UserComponents.Add(uc1);
u1.UserComponents.Add(uc2);
u2.UserComponents.Add(uc1);
u2.UserComponents.Add(uc3);
Console.Write(u1.UserComponents[0].Count); //Outputs 1
Console.Write(u1.UserComponents[1].Count); //Outputs 2
Console.Write(u2.UserComponents[0].Count); //Outputs 1
Console.Write(u2.UserComponents[1].Count); //Outputs 2
u2.UserComponents[0].Count = 5;
u2.UserComponents[1].Count = 6;
Console.Write(u1.UserComponents[0].Count); //Outputs 5
Console.Write(u1.UserComponents[1].Count); //Outputs 6
Console.Write(u2.UserComponents[0].Count); //Outputs 5
Console.Write(u2.UserComponents[1].Count); //Outputs 2
So your code to change values is fine, but when you build up your list, you need to create distinct UserComponents if they are not linked together.
Your first call to First() is wrong. Try it like this:
Users.Where((us) => us.UserType == "2")
.Select((us) => us.UserComponents)
.Where((uc) => uc.UserComponentKey == "XYZ")
.First()
.Count = value;
Suggestion: Why don't you make UserType an int?
May be it helps:
List<Users> _users = new List<Users>();
_users.Add(new Users() { UserType = "1", UserComponents = new List<UserComponent>() { new UserComponent() { Count = 0, UserComponentKey = "XYZ" } } });
_users.Add(new Users() { UserType = "2", UserComponents = new List<UserComponent>() { new UserComponent() { Count = 2, UserComponentKey = "XYZ" } } });
_users.Add(new Users() { UserType = "3", UserComponents = new List<UserComponent>() { new UserComponent() { Count = 5, UserComponentKey = "XYZ" } } });
_users.Where(us => us.UserType == "2").First().UserComponents.Where(uc => uc.UserComponentKey == "XYZ").First().Count = 356;
foreach (Users us in _users)
{
Console.WriteLine("UserType: " + us.UserType);
foreach (UserComponent uc in us.UserComponents)
{
Console.WriteLine("Key: {0} Value: {1}", uc.UserComponentKey, uc.Count);
}
}
Related
At the moment I'm retrieving data from the DB through a method that retrieves an IQueryable<T1>, filtering, sorting and then paging it (all these on the DB basically), before returning the result to the UI for display in a paged table.
I need to integrate results from another DB, and paging seems to be the main issue.
models are similar but not identical (same fields, different names, will need to map to a generic domain model before returning);
joining at the DB level is not possible;
there are ~1000 records at the moment between both DBs (added during
the past 18 months), and likely to grow at mostly the same (slow)
pace;
results always need to be sorted by 1-2 fields (date-wise).
I'm currently torn between these 2 solutions:
Retrieve all data from both sources, merge, sort and then cache them; then simply filter and page on said cache when receiving requests - but I need to invalidate the cache when the collection is modified (which I can);
Filter data on each source (again, at the DB level), then retrieve, merge, sort & page them, before returning.
I'm looking to find a decent algorithm performance-wise. The ideal solution would probably be a combination between them (caching + filtering at the DB level), but I haven't wrapped my head around that at the moment.
I think you can use the following algorithm. Suppose your page size is 10, then for page 0:
Get 10 results from database A, filtered and sorted at db level.
Get 10 results from database B, filtered and sorted at db level (in parallel with the above query)
Combine those two results to get 10 records in the correct sort order. So you have 20 records sorted, but take only first 10 of them and display in UI
Then for page 1:
Notice how many items from database A and B you used to display in UI at previous step. For example, you used 2 items from database A and 8 items from database B.
Get 10 results from database A, filtered and sorted, but starting at position 2 (skip 2), because those two you already have shown in UI.
Get 10 results from database B, filtered and sorted, but starting at position 8 (skip 8).
Merge the same way as above to get 10 records from 20. Suppose now you used 5 item from A and 5 items from B. Now, in total, you have shown 7 items from A and 13 items from B. Use those numbers for the next step.
This will not allow to (easily) skip pages, but as I understand that is not a requirement.
The perfomance should be effectively the same as when you are querying single database, because queries to A and B can be done in parallel.
I've created something here, I will come back with explications if needed.
I'm not sure my algorithm works correctly for all edge cases, it cover all of the cases what I had in mind, but you never know. I'll leave the code here for your pleasure, I will answer and explain what is done there if you need that, leave a comment.
And perform multiple tests with list of items with large gaps between the values.
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
//each time when this objects are accessed, consider as a database call
private static IQueryable<model1> dbsetModel_1;
private static IQueryable<model2> dbsetModel_2;
private static void InitDBSets()
{
var rnd = new Random();
List<model1> dbsetModel1 = new List<model1>();
List<model2> dbsetModel2 = new List<model2>();
for (int i = 1; i < 300; i++)
{
if (i % 2 == 0)
{
dbsetModel1.Add(new model1() { Id = i, OrderNumber = rnd.Next(1, 10), Name = "Test " + i.ToString() });
}
else
{
dbsetModel2.Add(new model2() { Id2 = i, OrderNumber2 = rnd.Next(1, 10), Name2 = "Test " + i.ToString() });
}
}
dbsetModel_1 = dbsetModel1.AsQueryable();
dbsetModel_2 = dbsetModel2.AsQueryable();
}
public static void Main()
{
//generate sort of db data
InitDBSets();
//test
var result2 = GetPage(new PagingFilter() { Page = 5, Limit = 10 });
var result3 = GetPage(new PagingFilter() { Page = 6, Limit = 10 });
var result5 = GetPage(new PagingFilter() { Page = 7, Limit = 10 });
var result6 = GetPage(new PagingFilter() { Page = 8, Limit = 10 });
var result7 = GetPage(new PagingFilter() { Page = 4, Limit = 20 });
var result8 = GetPage(new PagingFilter() { Page = 200, Limit = 10 });
}
private static PagedList<Item> GetPage(PagingFilter filter)
{
int pos = 0;
//load only start pages intervals margins from both database
//this part need to be transformed in a stored procedure on db one, skip, take to return interval start value for each frame
var framesBordersModel1 = new List<Item>();
dbsetModel_1.OrderBy(x => x.Id).ThenBy(z => z.OrderNumber).ToList().ForEach(i => {
pos++;
if (pos - 1 == 0)
{
framesBordersModel1.Add(new Item() { criteria1 = i.Id, criteria2 = i.OrderNumber, model = i });
}
else if ((pos - 1) % filter.Limit == 0)
{
framesBordersModel1.Add(new Item() { criteria1 = i.Id, criteria2 = i.OrderNumber, model = i });
}
});
pos = 0;
//this part need to be transformed in a stored procedure on db two, skip, take to return interval start value for each frame
var framesBordersModel2 = new List<Item>();
dbsetModel_2.OrderBy(x => x.Id2).ThenBy(z => z.OrderNumber2).ToList().ForEach(i => {
pos++;
if (pos - 1 == 0)
{
framesBordersModel2.Add(new Item() { criteria1 = i.Id2, criteria2 = i.OrderNumber2, model = i });
}
else if ((pos -1) % filter.Limit == 0)
{
framesBordersModel2.Add(new Item() { criteria1 = i.Id2, criteria2 = i.OrderNumber2, model = i });
}
});
//decide where is the position of your cursor based on start margins
//int mainCursor = 0;
int cursor1 = 0;
int cursor2 = 0;
//filter pages start from 1, filter.Page cannot be 0, if indeed you have page 0 change a lil' bit he logic
if (framesBordersModel1.Count + framesBordersModel2.Count < filter.Page) throw new Exception("Out of range");
while ( cursor1 + cursor2 < filter.Page -1)
{
if (framesBordersModel1[cursor1].criteria1 < framesBordersModel2[cursor2].criteria1)
{
cursor1++;
}
else if (framesBordersModel1[cursor1].criteria1 > framesBordersModel2[cursor2].criteria1)
{
cursor2++;
}
//you should't get here case main key sound't be duplicate, annyhow
else
{
if (framesBordersModel1[cursor1].criteria2 < framesBordersModel2[cursor2].criteria2)
{
cursor1++;
}
else
{
cursor2++;
}
}
//mainCursor++;
}
//magic starts
//inpar skipable
int skipEndResult = 0;
List<Item> dbFramesMerged = new List<Item>();
if ((cursor1 + cursor2) %2 == 0)
{
dbFramesMerged.AddRange(
dbsetModel_1.OrderBy(x => x.Id)
.ThenBy(z => z.OrderNumber)
.Skip(cursor1*filter.Limit)
.Take(filter.Limit)
.Select(x => new Item() {criteria1 = x.Id, criteria2 = x.OrderNumber, model = x})
.ToList()); //consider as db call EF or Stored Procedure
dbFramesMerged.AddRange(
dbsetModel_2.OrderBy(x => x.Id2)
.ThenBy(z => z.OrderNumber2)
.Skip(cursor2*filter.Limit)
.Take(filter.Limit)
.Select(x => new Item() {criteria1 = x.Id2, criteria2 = x.OrderNumber2, model = x})
.ToList());
; //consider as db call EF or Stored Procedure
}
else
{
skipEndResult = filter.Limit;
if (cursor1 > cursor2)
{
cursor1--;
}
else
{
cursor2--;
}
dbFramesMerged.AddRange(
dbsetModel_1.OrderBy(x => x.Id)
.ThenBy(z => z.OrderNumber)
.Skip(cursor1 * filter.Limit)
.Take(filter.Limit)
.Select(x => new Item() { criteria1 = x.Id, criteria2 = x.OrderNumber, model = x })
.ToList()); //consider as db call EF or Stored Procedure
dbFramesMerged.AddRange(
dbsetModel_2.OrderBy(x => x.Id2)
.ThenBy(z => z.OrderNumber2)
.Skip(cursor2 * filter.Limit)
.Take(filter.Limit)
.Select(x => new Item() { criteria1 = x.Id2, criteria2 = x.OrderNumber2, model = x })
.ToList());
}
IQueryable<Item> qItems = dbFramesMerged.AsQueryable();
PagedList<Item> result = new PagedList<Item>();
result.AddRange(qItems.OrderBy(x => x.criteria1).ThenBy(z => z.criteria2).Skip(skipEndResult).Take(filter.Limit).ToList());
//here again you need db cals to get total count
result.Total = dbsetModel_1.Count() + dbsetModel_2.Count();
result.Limit = filter.Limit;
result.Page = filter.Page;
return result;
}
}
public class PagingFilter
{
public int Limit { get; set; }
public int Page { get; set; }
}
public class PagedList<T> : List<T>
{
public int Total { get; set; }
public int? Page { get; set; }
public int? Limit { get; set; }
}
public class Item : Criteria
{
public object model { get; set; }
}
public class Criteria
{
public int criteria1 { get; set; }
public int criteria2 { get; set; }
//more criterias if you need to order
}
public class model1
{
public int Id { get; set; }
public int OrderNumber { get; set; }
public string Name { get; set; }
}
public class model2
{
public int Id2 { get; set; }
public int OrderNumber2 { get; set; }
public string Name2 { get; set; }
}
}
How do i match 2 objects in a list if their ID match, and their text doesn't?
my objects is added to a list:
List<MyObject> list = New List<MyObject>();
This could be my list (This is an object):
ID Text
1 this is some text
2 text1
1 more text
1 a little more
2 text 2
3 XXX
Then i would like the result to be:
ID Text
1 this is some text more text a little more
2 text1 text2
3 XXX
I've tried with a for in a for loop, but i just can figure it out..
for (int i = 0; i < OrderList.Count; i++)
{
bool existsMoreThanOnce = false;
for (int j = i; j < OrderList.Count; j++)
{
duplicates.Add(OrderList[i]);
if (OrderList[i].OrderNumber == OrderList[j].OrderNumber && OrderList[i].OrderText != OrderList[j].OrderText)
{
if(!uniques.Contains(OrderList[j]))
{
duplicates.Add(OrderList[j]);
existsMoreThanOnce = true;
}
}
}
if (existsMoreThanOnce == false)
{
uniques.Add(OrderList[i]);
}
}
First I create a class
public class Items
{
public int ID { get; set; }
public string Text { get; set; }
public Items(int id, string text)
{
ID = id;
Text = text;
}
}
Now I the logic of my code is
List<Items> objItems = new List<Items>();
objItems.Add(new Items(1,"Rahul"));
objItems.Add(new Items(2, "Rohit"));
objItems.Add(new Items(1, "Kumar"));
objItems.Add(new Items(2, "Verma"));
List<Items> objNew = new List<Items>(); //it will hold result
string str = "";
for (int i = 0; i < objItems.Count; i++)
{
if (objItems[i].ID > 0)
{
str = objItems[i].Text;
for (int j = i + 1; j < objItems.Count; j++)
{
if (objItems[i].ID == objItems[j].ID)
{
str += objItems[j].Text + " ";
objItems[j].ID = -1;
}
}
objNew.Add(new Items(objItems[i].ID, str));
}
}
ObjNew object contains the required output.
var result = list1.Concat(list2)
.GroupBy(x => x.ID)
.Where(g => g.GroupBy(x=>x.Text).Count() > 1)
.Select(x => x.Key)
.ToList();
You can start with LINQ's GroupBy.
var output = input.GroupBy(i => i.ID)
.Select(i => new { ID = i.Key,
Text = String.Join(" ",
i.Select(x => x.Text).ToArray()) });
First i created a class to hold your list
public class MyObject
{
public int ID { get; set; }
public string Text { get; set; }
}
then i inserted the dummy values into it
List<MyObject> obj = new List<MyObject>
{
new MyObject{ID=1, Text="this is some text"},
new MyObject{ID=2, Text="text1"},
new MyObject{ID=1, Text="more text"},
new MyObject{ID=1, Text="a little more"},
new MyObject{ID=2, Text="text 2"},
new MyObject{ID=3, Text="XXX"}
};
List<MyObject> obj2 = new List<MyObject>(); //this list will hold your output
//the linq query will filter out the uniques ids.
var uniqueIds = (from a in obj select new { a.ID, a.Text }).GroupBy(x => x.ID).ToList();
//then iterated through all the unique ids to merge the texts and list them under the unique ids.
int id=0;
foreach (var item in uniqueIds)
{
string contText = "";
for (int j = 0; j < item.Count(); j++)
{
contText += item.ElementAt(j).Text + " ";
id = item.ElementAt(j).ID;
}
obj2.Add(new MyObject { ID = id, Text = contText });
}
the list obj2 will have your desired output.
I have a class:
public class ShipmentInformation
{
public string OuterNo { get; set; }
public long Start { get; set; }
public long End { get; set; }
}
I have a List<ShipmentInformation> variable called Results.
I then do:
List<ShipmentInformation> FinalResults = new List<ShipmentInformation>();
var OuterNumbers = Results.GroupBy(x => x.OuterNo);
foreach(var item in OuterNumbers)
{
var orderedData = item.OrderBy(x => x.Start);
ShipmentInformation shipment = new ShipmentInformation();
shipment.OuterNo = item.Key;
shipment.Start = orderedData.First().Start;
shipment.End = orderedData.Last().End;
FinalResults.Add(shipment);
}
The issue I have now is that within each grouped item I have various ShipmentInformation but the Start number may not be sequential by x. x can be 300 or 200 based on a incoming parameter. To illustrate I could have
Start = 1, End = 300
Start = 301, End = 600
Start = 601, End = 900
Start = 1201, End = 1500
Start = 1501, End = 1800
Because I have this jump I cannot use the above loop to create an instance of ShipmentInformation and take the first and last item in orderedData to use their data to populate that instance.
I would like some way of identifying a jump by 300 or 200 and creating an instance of ShipmentInformation to add to FinalResults where the data is sequnetial.
Using the above example I would have 2 instances of ShipmentInformation with a Start of 1 and an End of 900 and another with a Start of 1201 and End of 1800
Try the following:
private static IEnumerable<ShipmentInformation> Compress(IEnumerable<ShipmentInformation> shipments)
{
var orderedData = shipments.OrderBy(s => s.OuterNo).ThenBy(s => s.Start);
using (var enumerator = orderedData.GetEnumerator())
{
ShipmentInformation compressed = null;
while (enumerator.MoveNext())
{
var current = enumerator.Current;
if (compressed == null)
{
compressed = current;
continue;
}
if (compressed.OuterNo != current.OuterNo || compressed.End < current.Start - 1)
{
yield return compressed;
compressed = current;
continue;
}
compressed.End = current.End;
}
if (compressed != null)
{
yield return compressed;
}
}
}
Useable like so:
var finalResults = Results.SelectMany(Compress).ToList();
If you want something that probably has terrible performance and is impossible to understand, but only uses out-of-the box LINQ, I think this might do it.
var orderedData = item.OrderBy(x => x.Start);
orderedData
.SelectMany(x =>
Enumerable
.Range(x.Start, 1 + x.End - x.Start)
.Select(n => new { time = n, info = x))
.Select((x, i) => new { index = i, time = x.time, info = x.info } )
.GroupBy(t => t.time - t.info)
.Select(g => new ShipmentInformation {
OuterNo = g.First().Key,
Start = g.First().Start(),
End = g.Last().End });
My brain hurts.
(Edit for clarity: this just replaces what goes inside your foreach loop. You can make it even more horrible by putting this inside a Select statement to replace the foreach loop, like in rich's answer.)
How about this?
List<ShipmentInfo> si = new List<ShipmentInfo>();
si.Add(new ShipmentInfo(orderedData.First()));
for (int index = 1; index < orderedData.Count(); ++index)
{
if (orderedData.ElementAt(index).Start ==
(si.ElementAt(si.Count() - 1).End + 1))
{
si[si.Count() - 1].End = orderedData.ElementAt(index).End;
}
else
{
si.Add(new ShipmentInfo(orderedData.ElementAt(index)));
}
}
FinalResults.AddRange(si);
Another LINQ solution would be to use the Except extension method.
EDIT: Rewritten in C#, includes composing the missing points back into Ranges:
class Program
{
static void Main(string[] args)
{
Range[] l_ranges = new Range[] {
new Range() { Start = 10, End = 19 },
new Range() { Start = 20, End = 29 },
new Range() { Start = 40, End = 49 },
new Range() { Start = 50, End = 59 }
};
var l_flattenedRanges =
from l_range in l_ranges
from l_point in Enumerable.Range(l_range.Start, 1 + l_range.End - l_range.Start)
select l_point;
var l_min = 0;
var l_max = l_flattenedRanges.Max();
var l_allPoints =
Enumerable.Range(l_min, 1 + l_max - l_min);
var l_missingPoints =
l_allPoints.Except(l_flattenedRanges);
var l_lastRange = new Range() { Start = l_missingPoints.Min(), End = l_missingPoints.Min() };
var l_missingRanges = new List<Range>();
l_missingPoints.ToList<int>().ForEach(delegate(int i)
{
if (i > l_lastRange.End + 1)
{
l_missingRanges.Add(l_lastRange);
l_lastRange = new Range() { Start = i, End = i };
}
else
{
l_lastRange.End = i;
}
});
l_missingRanges.Add(l_lastRange);
foreach (Range l_missingRange in l_missingRanges) {
Console.WriteLine("Start = " + l_missingRange.Start + " End = " + l_missingRange.End);
}
Console.ReadKey(true);
}
}
class Range
{
public int Start { get; set; }
public int End { get; set; }
}
I'm writing a program that needs to perform a search as one of the required functions. The user should be able to use any number of fields, ranging from none to all of them (total of 7). So far I've had success grouping each case of data input within an if statement like so:
List<TypeClass> myList = new List<TypeClass>
foreach TypeClass currentItem in myList
{
if (data1 == currentItem.GetData1() || data1 == "Do Not Search" && (data2 == currentItem.GetData2() || data2 == "Do Not Search") && (data3...)
{
//Stuff
}
}
If you notice, I grouped each data field within brackets, so the statement can only be satisfied if each of the entered data is either the needed condition, or an 'empty field'. However, I can't group the very first portion of the statement as I do with the other data2,3,4... Instead the statement always gets evaluated to true even if there are other search fields that do not satisfy the conditions of the statement. If I use additional brackets the program completely ignores the if statement and treats it as if none of the cases match at all.
So if I write it like this:
if ((data1 == currentIten.GetData1 || data1 == "Do Not Search") && (data2...)
Nothing gets checked and the statement is ignored. Is this normal? Are there any better/more efficient ways of dealing with optional search field selection?
EDIT: Sorry for the typo, each GetDataX is an accessor and I forgot to write the parentheses ()
You could do it like this for an or condition
List<string> mylist = new List<string>();
string data1 = "test1";
string data2 = "test2";
string data3 = "test3";
string data4 = "test4";
foreach (string s in mylist)
{
bool found = false;
if(data1.Equals(s) || data1.Equals("Do not Search"))
{
found = true;
}
if (data2.Equals(s) || data1.Equals("Do not Search"))
{
found = true;
}
if (data3.Equals(s) || data1.Equals("Do not Search"))
{
found = true;
}
if (data4.Equals(s) || data1.Equals("Do not Search"))
{
found = true;
}
}
Or like this for an and condition
List<string> mylist = new List<string>();
string data1 = "test1";
string data2 = "test2";
string data3 = "test3";
string data4 = "test4";
foreach (string s in mylist)
{
bool found = false;
bool notfound = false;
if(data1.Equals(s) || data1.Equals("Do not Search"))
{
found = true;
}
else
{
notfound = true;
}
if (data2.Equals(s) || data1.Equals("Do not Search"))
{
found = true;
}
else
{
notfound = true;
}
if (data3.Equals(s) || data1.Equals("Do not Search"))
{
found = true;
}
else
{
notfound = true;
}
if (data4.Equals(s) || data1.Equals("Do not Search"))
{
found = true;
}
else
{
notfound = true;
}
// Force all to match
if (notfound)
return null;
}
My Preference would be something like this though where you can leverage search functions to do what you need to.....
List<string> mylist = new List<string>();
List<string> mysearches = new List<string>();
string data1 = "test1";
string data2 = "test2";
string data3 = "test3";
string data4 = "test4";
if(data1 != "Do not Search")
mysearches.Add(data1);
if (data2 != "Do not Search")
mysearches.Add(data2);
if (data3 != "Do not Search")
mysearches.Add(data3);
if (data4 != "Do not Search")
mysearches.Add(data4);
bool found = false;
bool andconditionmatch = true;
foreach (string s in mylist)
{
if (mysearches.Contains(s))
{
found = true;
}
else
{
andconditionmatch = false;
}
}
Put all the possibilities in hashset .
Hashset with all possibilities.
foreach(item in selectedItems) //you may not need this if you dont have to perform action forall selected items.
{
if (Hashset contains item)
//do stuff.
}
I would move the matching inside the class and instead of dealing with 7 separate possible values to match, make them one class instance called criteria. See sample code below:
public enum States
{
None,
Tenesee,
Georgia,
Colorado,
Florida
}
class Item
{
public States State { get; set; }
public string Name { get; set; }
public int ID { get; set; }
public bool IsMatch(Item criteria)
{
bool match = true;
if (criteria.State != States.None) match &= criteria.State == State;
if (!string.IsNullOrEmpty(criteria.Name)) match &= criteria.Name.Equals(Name);
if (criteria.ID > 0) match &= criteria.ID == ID;
return match;
}
public override string ToString()
{
return string.Format("ID={0}, Name={1}, State={2}", ID.ToString(), Name, State.ToString());
}
}
class Program
{
static void Main(string[] args)
{
List<Item> list = new List<Item>();
list.Add(new Item() { ID = 10016, Name = "Julia", State = States.Georgia });
list.Add(new Item() { ID = 10017, Name = "Scott", State = States.Colorado });
list.Add(new Item() { ID = 10018, Name = "Samantha", State = States.Tenesee });
list.Add(new Item() { ID = 10019, Name = "Julia", State = States.Florida });
Item criteria = new Item()
{
State = States.Tenesee,
ID = 10018
};
List<Item> selection = list.FindAll((item) => item.IsMatch(criteria));
foreach (var item in selection)
{
Console.WriteLine("{0}", item);
}
}
}
With the result
ID=10018, Name=Samantha, State=Tenesee
So you build a criteria instance Item and compare for a match if a property is well defined. The loop through all the items and select the ones that match the criteria. Obviously you have to extend .IsMatch() for all 7 properties.
I have CreateDiscountViewByUser discountViewByUser it contains a list of cities that are chosen by the user, but they may already be those cities that have been added.
List<DiscountCity> discountCities = (from city in db.DiscountCities
where city.DiscountId == discountViewByUser.Id
select city).ToList();
for (int y = 0; y < discountCities.Count(); y++)
{
var dc = discountCities[y];
bool flag = false;
for (int i = 0; i < discountViewByUser.DiscountCitys.Length; i++)
{
if (dc.CityId == discountViewByUser.DiscountCitys[i])
{
flag = true;
discountCities.Remove(dc);
y--;
}
}
if (!flag)
{
db.DiscountCities.DeleteObject(dc);
}
}
foreach (var dc in discountCities)
{
DiscountCity discountCity = new DiscountCity
{Id = Guid.NewGuid(),
CityId = dc.CityId,
DiscountId = main.Id};
db.DiscountCities.AddObject(discountCity);
}
how to add only the new city?
My code does not work = (
UPDATE:
discountViewByUser.DiscountCitys type int[].
db.DiscountCities table: Id DiscountId CityId.
example:
in database: Odessa, Kiev
user set: Odessa, Moscow.
I need delete Kiev and add moscow how do this?
What I recommend is adding all the items and then removing duplicates.
// Where uniqueList is a List<T> of unique items:
uniqueList.AddRange(valuesToAdd);
uniqueList = uniqueList.Distinct(new CityEqualityComparer()).ToList();
// Sorry, I don't know how this would fit into your code
Since you are comparing cities by their CityId's, you will probably need to use a custom IEqualityComparer to determine which cities are duplicates.
Here is an example of such a class:
class CityEqualityComparer : IEqualityComparer<City>
{
public bool Equals(City arg1, City arg2)
{
return arg1.CityId == arg2.CityId;
}
public int GetHashCode(City arg)
{
return arg.CityId;
}
}
This question may also be of some help.
I suggest you do this in 2 steps.
1) Find the cities to be deleted
var deleteCities = db.DiscountCities.Where(c => c.DiscountId == discountViewByUser.Id
&& !discountViewByUser.DiscountCitys.Contains(c.CityId));
foreach(deleteCity in deleteCities)
{
db.DiscountCities.DeleteObject(deleteCity);
}
2) Find cityId's to be inserted
var insertCities = discountViewByUser.DiscountCitys.Except(
db.DiscountCities.Where(c => c.DiscountId == discountViewByUser.Id)
.Select(c => c.CityId));
foreach(var insertCity in insertCities)
{
DiscountCity discountCity = new DiscountCity
{Id = Guid.NewGuid(), CityId = insertCity, DiscountId = discountViewByUser.Id};
db.DiscountCities.AddObject(discountCity);
}