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);
}
Related
I'm working on an algorithm which can generate 2 types of recommendations, restaurants and dishes. All of this works fine, but I wanted to merge these 2 types of recommendations in a single list, which is where I encountered some issues. From my previous question I concluded that I needed a wrapper class, which I have set up like this:
public class RecommenderItem
{
public Guid Id { get; set; }
public object Entity { get; set; }
}
Now I want to alternate the 2 types of recommendations so the list would look like this:
[Restaurant][Dish][Restaurant][Dish][Restaurant][Dish] //Etc...
Note that these recommendations are completely separate. They are generated purely based on the user's preference, and they have no correlation in between them. My product owner wants to show these recommendations on the home page of our app like this.
These lists are different in length, so if I have added all items from a list, I wanted to just add the remaining objects from the other list. A possible scenario of this could look like this:
/*Other objects before this...*/[Dish][Restaurant][Dish][Dish][Dish] //Etc...
Here did the list of restaurant objects run out and I just wanted to add the remaining dish recommendations at the end of the list.
I have gotten this far, but I'm unsure how I would catch an IndexOutOfBounds exception and add the rest of the remaining objects at the end.
public List<RecommenderItem> GetMergedRecommendationLists(List<Restaurant> restaurantRecommendations,
List<Dish> dishRecommendations)
{
//Setting up the output list.
List<RecommenderItem> output = new List<RecommenderItem>();
int count = 0;
//Check which list is longer and use that count
if (restaurantRecommendations.Count > dishRecommendations.Count)
count = dishRecommendations.Count;
else
count = restaurantRecommendations.Count;
for (int i = 0; i < count; i++)
{
//I'm fully aware this isn't the most optimal way of doing this,
//but I'm only looking at functionality here, optimizing performance comes later.
var restRecommendation = restaurantRecommendations[i];
var dishRecommendation = dishRecommendations[i];
output.Add(new RecommenderItem()
{
Id = restRecommendation.Id,
Entity = restRecommendation
});
output.Add(new RecommenderItem()
{
Id = dishRecommendation.Id,
Entity = dishRecommendation
});
}
return output;
}
Does anyone have an idea how I could do this? Could I just catch an IndexOutOfBounds exception and use .AddRange() for the remaining objects? I'm not sure how I could check which list was out of bounds.
Let me know if I should elaborate more and thanks in advance!
Edit: -removed because it wasn't fair.-
This is a fairly succinct way of doing this.
While not Linq, it works in the spirit of the way Linq works by deferring doing any work until the resulting sequence is enumerated:
public static IEnumerable<RecommenderItem> Merge(IEnumerable<Restaurant> restaurants, IEnumerable<Dish> dishes)
{
using (var r = restaurants.GetEnumerator())
using (var d = dishes.GetEnumerator())
{
while (true)
{
bool rAvailable = r.MoveNext();
bool dAvailable = d.MoveNext();
if (rAvailable)
yield return new RecommenderItem { Id = r.Current.Id, Entity = r.Current };
if (dAvailable)
yield return new RecommenderItem { Id = d.Current.Id, Entity = d.Current };
if (!rAvailable && !dAvailable)
break;
}
}
}
If you happen to be using the MoreLinq NuGet package that includes the ZipLongest extension method, you can use the following simplified implementation instead:
public static IEnumerable<RecommenderItem> Merge(IEnumerable<Restaurant> restaurants, IEnumerable<Dish> dishes)
{
foreach (var item in restaurants.ZipLongest(dishes, (r, d) => new { r, d }))
{
if (item.r != null)
yield return new RecommenderItem { Id = item.r.Id, Entity = item.r };
if (item.d != null)
yield return new RecommenderItem { Id = item.d.Id, Entity = item.d };
}
}
Addendum
As #InBetween posted in his answer, you can put the interleaving logic into an extension method. Here's my version; it's substantially the same, except I've added a small optimisation to avoid calling .MoveNext() when its not necessary:
public static class EnumerableExt
{
public static IEnumerable<T> Interleave<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
using (var ae = a.GetEnumerator())
using (var be = b.GetEnumerator())
{
bool aAvailable = true;
bool bAvailable = true;
while (aAvailable || bAvailable)
{
aAvailable = aAvailable && ae.MoveNext();
bAvailable = bAvailable && be.MoveNext();
if (aAvailable)
yield return ae.Current;
if (bAvailable)
yield return be.Current;
}
}
}
}
Once you have that, I realised that you don't need to write an implict operator. Instead, you can just convert the two sequences to the resultant type before calling Interleave() like so:
var restaurantsAsRecommenderItems =
restaurantRecommendations
.Select(r => new RecommenderItem {Id = r.Id, Entity = r});
var dishesAsRecommenderItems =
dishRecommendations
.Select(d => new RecommenderItem {Id = d.Id, Entity = d});
var result =
restaurantsAsRecommenderItems
.Interleave(dishesAsRecommenderItems)
.ToList();
My recommendation would be to just make simple implicit operator :
public static implicit operator RecommenderItem(Restaurant restaurant) {
return new RecommenderItem { Id = restaurant.Id, Entity = restaurant };
}
Then you have possibility to convert these types easily like :
Restaurant rest = //...
RecommenderItem rItem = rest; // here the implicit operator is called
After doing this you can just use one for loop :
int count = Math.Max(restaurantRecommendations.Count, dishRecommendations.Count);
for ( int i = 0; i < count; i++ ) {
if ( i < restRecommendations.Count )
output.Add(restRecommendations[i]);
if ( i < dishRecommendations.Count )
output.Add(dishRecommendations[i]);
}
This will make your work much more easier.
Well, there are probably more elegant LINQ solutions but you have already most, it's also a very efficient approach:
public List<RecommenderItem> GetMergedRecommendationLists(List<Restaurant> restaurantRecommendations, List<Dish> dishRecommendations)
{
//Setting up the output list.
List<RecommenderItem> output = new List<RecommenderItem>();
int count = Math.Min(restaurantRecommendations.Count, dishRecommendations.Count);
for (int i = 0; i < count; i++)
{
var restRecommendation = restaurantRecommendations[i];
var dishRecommendation = dishRecommendations[i];
output.Add(new RecommenderItem()
{
Id = restRecommendation.Id,
Entity = restRecommendation
});
output.Add(new RecommenderItem()
{
Id = dishRecommendation.Id,
Entity = dishRecommendation
});
}
int remainingRestaurant = restaurantRecommendations.Count - count;
int remainingDishes = dishRecommendations.Count - count;
if (remainingRestaurant > 0)
{
for (int i = count; i < restaurantRecommendations.Count; i++)
{
var restRecommendation = restaurantRecommendations[i];
output.Add(new RecommenderItem()
{
Id = restRecommendation.Id,
Entity = restRecommendation
});
}
}
else if (remainingDishes > 0)
{
for (int i = count; i < dishRecommendations.Count; i++)
{
var dishRecommendation = dishRecommendations[i];
output.Add(new RecommenderItem()
{
Id = dishRecommendation.Id,
Entity = dishRecommendation
});
}
}
return output;
}
A simple way of doing it would be:
public static IEnumerable<T> Merge<T>(this IEnumerable<T> first, IEnumerable<T> second)
{
using (var firstEnumerator = first.GetEnumerator())
using (var secondEnumerator = second.GetEnumerator())
{
while (firstEnumerator.MoveNext())
{
yield return firstEnumerator.Current;
if (secondEnumerator.MoveNext())
{
yield return secondEnumerator.Current;
}
}
while (secondEnumerator.MoveNext())
{
yield return secondEnumerator.Current;
}
}
}
After having created two arrays of restaurants and dishes of the same type RecommenderItem, you can use the Zip method like :
var restaurants = restaurantRecommendations.Select(x => new RecommenderItem {
Id = x.Id,
Entity = x
}).ToArray();
var dishes = dishRecommendations.Select(x => new RecommenderItem {
Id = x.Id,
Entity = x
}).ToArray();
var output = restaurants.Zip(dishes, (r, d) => new[] { r, d })
.SelectMany(r => r).Concat(dishes.Skip(restaurants.Length))
.Concat(restaurants.Skip(dishes.Length));
Restaraunt and Dish would have to share a base type:
restaurantRecommendations.Select(item => new RecommenderItem()
{
Id = item.Id,
Entity = item
});
dishRecommendations.Select(item => new RecommenderItem()
{
Id = item.Id,
Entity = item
});
Once that's the case you could use something like this slightly modified version of Zip (from System.Linq):
private static IEnumerable<T> ZipThrough<T>(IEnumerable<T> first, IEnumerable<T> second)
{
if (first == null) throw new ArgumentNullException(nameof(first));
if (second == null) throw new ArgumentNullException(nameof(second));
using (var e1 = first.GetEnumerator())
{
using (var e2 = second.GetEnumerator())
{
while (true)
if (e1.MoveNext())
{
yield return e1.Current;
if (e2.MoveNext()) yield return e2.Current;
}
else if (e2.MoveNext())
{
yield return e2.Current;
}
else
{
break;
}
}
}
}
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);
}
}
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'm rather new to Linq. I'm having trouble coding this.
I have a list with many different sublists.
oldList[0] some type
oldList[1] another different type
oldList[2] the type I want
oldList[3] more types
I want to select all the parameters from a specific type and write them to a temp list.
If that temp list is empty, I want to assign some values (values don't actually matter).
After changing the values, I want to write temp list back into oldList.
Please advise. This is a huge learning experience for me.
public void myFunction(list)
{
//list contains at least 5 sublists of various type
//check if the type I want is null
IEnumerable<TypeIWant> possiblyEmptyList = list.OfType<TypeIWant>(); //find the type I want from the list and save it
if (possiblyEmptyList == null) //this doesn't work and possiblyEmptyList.Count <= 1 doesn't work
{
//convert residence address to forwarding address
IEnumerable<ReplacementType> replacementList = list.OfType<ReplacementType>();
forwardingAddress = replacementList.Select(x => new TypeIWant /* this statement functions exactly the way I want it to */
{
Address1 = x.Address1,
Address2 = x.Address2,
AddressType = x.AddressType,
City = x.City,
CountryId = x.CountryId,
CountyRegion = x.CountyRegion,
Email = x.Email,
ConfirmEmail = x.ConfirmEmail,
Fax = x.Fax,
Telephone = x.Telephone,
State = x.State,
PostalCode = x.PostalCode
});
//write forwarding address back to list
//don't know how to do this
}
LINQ purpose is querying. It doesn't allow you to replace some items in collection with other items. Use simple loop instead:
IEnumerable<TypeIWant> possiblyEmptyList = list.OfType<TypeIWant>();
if (!possiblyEmptyList.Any())
{
for (int i = 0; i < list.Count; i++)
{
ReplacementType item = list[i] as ReplacementType;
if (item == null)
continue;
list[i] = ConvertToTypeIWant(item);
}
}
And conversion (which is better to do with something like automapper):
private TypeIWant ConvertToTypeIWant(ReplacementType x)
{
return new TypeIWant
{
Address1 = x.Address1,
Address2 = x.Address2,
AddressType = x.AddressType,
City = x.City,
CountryId = x.CountryId,
CountyRegion = x.CountyRegion,
Email = x.Email,
ConfirmEmail = x.ConfirmEmail,
Fax = x.Fax,
Telephone = x.Telephone,
State = x.State,
PostalCode = x.PostalCode
};
}
Not LINQ but an example.
class Program
{
static void Main(string[] args)
{
// Vars
var list = new List<List<string>>();
var a = new List<string>();
var b = new List<string>();
var c = new List<string> { "one", "two", "three" };
var d = new List<string>();
// Add Lists
list.Add(a);
list.Add(b);
list.Add(c);
list.Add(d);
// Loop through list
foreach (var x in list)
{
if (x.Count < 1)
{
var tempList = new List<string>();
tempList.Add("some value");
x.Clear();
x.AddRange(tempList);
}
}
// Print
int count = 0;
foreach (var l in list)
{
count++;
Console.Write("(List " + count + ") ");
foreach (var s in l)
{
Console.Write(s + " ");
}
Console.WriteLine("");
}
}
}
(List 1) some value
(List 2) some value
(List 3) one two three
(List 4) some value
I have the following code:
List<ProductGroupProductData> productGroupProductDataList = FillMyList();
string[] excludeProductIDs = { "871236", "283462", "897264" };
int count = productGroupProductDataList.Count;
for (int removeItemIndex = 0; removeItemIndex < count; removeItemIndex++)
{
if (excludeProductIDs.Contains(productGroupProductDataList[removeItemIndex].ProductId))
{
productGroupProductDataList.RemoveAt(removeItemIndex);
count--;
}
}
Now i want to do the same with linq. Is there any way for this?
The second thing would be, to edit each List-Item property with linq.
you could use RemoveAll.
Example:
//create a list of 5 products with ids from 1 to 5
List<Product> products = Enumerable.Range(1,5)
.Select(c => new Product(c, c.ToString()))
.ToList();
//remove products 1, 2, 3
products.RemoveAll(p => p.id <=3);
where
// our product class
public sealed class Product {
public int id {get;private set;}
public string name {get; private set;}
public Product(int id, string name)
{
this.id=id;
this.name=name;
}
}
Firstly corrected version of your current code that won't skip entries
List<ProductGroupProductData> productGroupProductDataList = FillMyList();
string[] excludeProductIDs = { "871236", "283462", "897264" };
int count = productGroupProductDataList.Count;
for (int removeItemIndex = 0; removeItemIndex < count; removeItemIndex++)
{
while (removeItemIndex < count && excludeProductIDs.Contains(productGroupProductDataList[removeItemIndex].ProductId)) {
productGroupProductDataList.RemoveAt(removeItemIndex);
count--;
}
}
}
This linq code would do the job.
List<ProductGroupProductData> productGroupProductDataList = FillMyList();
string[] excludeProductIDs = { "871236", "283462", "897264" };
productGroupProductDataList=productGroupProductDataList.Where(x=>!excludedProductIDs.Contains(x.ProductId)).ToList();
Alternatively using paolo's answer of remove all the last line would be would be
productGroupProductDataList.RemoveAll(p=>excludedProductIDs.Contains(p=>p.ProductId));
What you mean by "The second thing would be, to edit each List-Item property with linq."?
As per your comment here's a version that creates a set that excludes the elements rather than removing them from the original list.
var newSet = from p in productGroupProductDataList
where !excludeProductIDs.Contains(p.ProductId))
select p;
The type of newSet is IEnumerable if you need (I)List you can easily get that:
var newList = newSet.ToList();