Merge 2 instances of same class based on ID using Linq/c# - c#

I have an application which gets 2 instances of the same class from different method.I want to merge the 2 instance into 1 instance of the class. Below is the format of the instances
Instance 1
[{
"locationId": "ABCD",
"Fruits":
{
Fruit:
{
TypeId: "Mango",
}
}
},
{
"locationId": "EFGH",
"Fruits":
{
Fruit:
{
TypeId: "Pear",
}
}
}]
Instance 2
[{
"locationId": "ABCD",
"Fruits":
{
Fruit:
{
TypeId: "Apple",
}
}
},
{
"locationId": "EFGH",
"Fruits":
{
Fruit:
{
TypeId: "Kiwi",
}
}
}]
I want to merge them so that they appear as below,
[{
"locationId": "ABCD",
"Fruits":
{
Fruit:
{
TypeId: "Mango",
},
Fruit:
{
TypeId: "Apple",
}
}
},
{
"locationId": "EFGH",
"Fruits":
{
Fruit:
{
TypeId: "Pear",
},
Fruit:
{
TypeId: "Kiwi",
}
}
}]
Is it possible using linq? Could anyone please direct me to the correct path?

So you have two sequence of objects, where every object has a LocationId and a Fruit (or an array of Fruits).
You want a LINQ statement, that results in a sequence of objects where every object has a LocationId, and a sequence of all Fruits on that location.
For this we use Enumerable.GroupBy
You forgot to tell us what type of input is. Let's name it:
class Fruit {...}
class FruitOnLocation
{
public string LocationId {get; set;}
public Fruit Fruit {get; set;}
}
It might be that you have a sequence of Fruits on this location. The code will be very similar.
IEnumerable<FruitOnLocation> instance1 = ...
IEnumerable<FruitOnLocation> instance2 = ...
var result = instance1.Concat(instance2) // concatenate the two sequences
.GroupBy(item => item.LocationId, // make groups of objects with same LocationId
(locationId, fruitsOnLocation) => new // from every locationId and all fruitsOnLocation
{ // with this locationId make one new
LocationId = locationId, // object containing the common locationId
Fruits = fruitsOnLocation // and the list of all Fruits with this
.ToList(), // locationId
})

I would join the two lists on locationId and than concat the fruits.
var list1 = new[]
{
new LocationFruit { locationId = "ABCD", Fruits = new List<Fruit> { new Fruit { TypeId = "Mango"} }},
new LocationFruit { locationId = "EFGH", Fruits = new List<Fruit> { new Fruit { TypeId = "Pear"} }}
};
var list2 = new[]
{
new LocationFruit { locationId = "ABCD", Fruits = new List<Fruit> { new Fruit { TypeId = "Apple"} }},
new LocationFruit { locationId = "EFGH", Fruits = new List<Fruit> { new Fruit { TypeId = "Kiwi"} }}
};
var result = from a in list1
join b in list2 on a.locationId equals b.locationId
select new LocationFruit
{
locationId = a.locationId,
Fruits = a.Fruits.Concat(b.Fruits).ToList()
};
Assumed your classes looked like this:
public class LocationFruit
{
public string locationId { get; set; }
public List<Fruit> Fruits { get; set; }
}
public class Fruit
{
public string TypeId { get; set; }
}
Or if you want non matched items from both lists:
var result = list1
.Concat(list2)
.GroupBy(
x => x.locationId,
(key, items) => new LocationFruit
{
locationId = key,
Fruits = items.SelectMany(y => y.Fruits).ToList()
});

I think that you might want to have/use classes as follows:
class StockEntry
{
public IEnumerable<PerLocationStock> Stocks { get; set; }
}
class PerLocationStock
{
public string LocationId { get; set; }
public IEnumerable<Fruit> Fruits { get; set; }
}
public class Fruit
{
public string TypeId { get; }
public Fruit(string typeId)
{
TypeId = typeId;
}
protected bool Equals(Fruit other) => string.Equals(TypeId, other.TypeId);
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Fruit) obj);
}
public override int GetHashCode() => (TypeId != null ? TypeId.GetHashCode() : 0);
}
Where your object initialistion would look as follows:
var instance1 = new StockEntry
{
Stocks = new[] {
new PerLocationStock
{
LocationId = "ABCD",
Fruits = new [] { new Fruit("Mango") }
},
new PerLocationStock
{
LocationId = "EFGH",
Fruits = new [] { new Fruit("Pear") }
}
}
};
var instance2 = new StockEntry
{
Stocks = new[] {
new PerLocationStock
{
LocationId = "ABCD",
Fruits = new [] { new Fruit("Apple") }
},
new PerLocationStock
{
LocationId = "EFGH",
Fruits = new [] { new Fruit("Kiwi"), new Fruit("Pear") }
}
}
};
And finally your merging objects algorithms would look as follows:
var mergedInstance = new[] { instance1, instance2 }
.SelectMany(se => se.Stocks)
.GroupBy(pls => pls.LocationId)
.Aggregate(
new StockEntry { Stocks = Enumerable.Empty<PerLocationStock>() },
(stockEntry, groupedByLocationFruits) =>
{
var perLocationStock = new PerLocationStock
{
LocationId = groupedByLocationFruits.Key,
Fruits = groupedByLocationFruits.SelectMany(x => x.Fruits).Distinct()
};
stockEntry.Stocks = new List<PerLocationStock>(stockEntry.Stocks) { perLocationStock };
return stockEntry;
});
Next loop over the result in a following way:
foreach (var locationStock in mergedInstance.Stocks)
{
Console.WriteLine($"{nameof(PerLocationStock.LocationId)}={locationStock.LocationId}");
foreach (var fruitsInLocation in locationStock.Fruits)
Console.WriteLine($"{nameof(Fruit.TypeId)}={fruitsInLocation.TypeId}");
}
And finally get your expected result:
LocationId=ABCD
TypeId=Mango
TypeId=Apple
LocationId=EFGH
TypeId=Pear
TypeId=Kiwi
Please note that duplicates were removed from the fruits thanks to the Fruit.Equals.

Related

Mongodb C# Update element in an multiple array with multiple values

I want to update the single document in collection with the guid as filter and update value is cityType. Every guid has different citytype here i have used 3 types it may be more.
So please give a right implementation using c# code.
Models:
public class Country
{
[BsonId]
public ObjectId Id { get; set; }
public int CountryId {get; set; }
public IEnumerable<States> States { get; set; }
}
public class States
{
public Guid Guid { get; set; }
public CityType CityType { get; set; }
}
Enum CityType
{
Unknown = 0,
Rural = 1,
Urban = 2
}
Existing Collection:
{
"_id": ObjectId("6903ea4d2df0c5659334e763"),
"CountryId": 200,
"States": [
{
"Guid": "AFCC4BE7-7585-5E46-A639-52F0537895D8",
"CityType": 0,
},
{
"Guid": "208FB603-08C7-46D9-B0C0-7AF4F691A96D",
"CityType": 0,
}
}
Input:
List<States>()
{
new States()
{
Guid = "AFCC4BE7-7585-5E46-A639-52F0537895D8",
CityType = CityType.Rural
},
new States()
{
Guid = "208FB603-08C7-46D9-B0C0-7AF4F691A96D",
CityType = CityType.Urban
}
}
Expected:
{
"_id": ObjectId("6903ea4d2df0c5659334e763"),
"CountryId": 200,
"States": [
{
"Guid": "AFCC4BE7-7585-5E46-A639-52F0537895D8",
"CityType": 1,
},
{
"Guid": "208FB603-08C7-46D9-B0C0-7AF4F691A96D",
"CityType": 2,
}
}
This is the method I have tried:
public async Task<bool> UpdateType(int countryId, IEnumerable<States> states)
{
var collection = connectionFactory.GetCollection<Country>(collectionName);
var cityTypes = states.Select(x => x.CityType);
var filter = Builders<Country>.Filter.Empty;
var update = Builders<Country>.Update.Set("States.$[edit].CityType", cityTypes);
var arrayFilters = new List<ArrayFilterDefinition>();
foreach (var state in states)
{
ArrayFilterDefinition<Country> optionsFilter = new BsonDocument("state.Guid", new BsonDocument("$eq", state.Guid));
arrayFilters.Add(optionsFilter);
}
var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };
var result = await collection.UpdateOneAsync(filter, update, updateOptions);
return result;
}
hope all details I have added here. Thanks in advance.
You don't have to loop through it:
Let's say you have a Class1 like this:
class Question : AuditableEntity {
public string Text { get; set; }
public List<string> Tags { get; set; } = new List<string>();
so you just say:
await collection.UpdateOneAsync(
someFilter,
Builders<Class1>.Update
.Set(f => f.Text, request.Question.Text)
.Set(f => f.Tags, request.Question.Tags));

How to merge two different List

I have a List<string> list1, example values:
var list1 = new List<string>()
{
"123", "1234", "12345",
};
I have a class:
public class TestClass {
public string name{ get; set; }
public int count{ get; set; }
}
and I have a List<TestClass> list2, example values:
var list2 = new List<TestClass>()
{
new TestClass() { name = "12", count = 0 },
new TestClass() { name = "123", count = 5 },
new TestClass() { name = "1234", count = 20 },
};
I want to merge list1 and list2 and the result should be:
name count
12 0
123 5
1234 20
12345 0
This works nicely:
var list1 = new List<string>()
{
"123", "1234", "12345",
};
var list2 = new List<TestClass>()
{
new TestClass() { name = "12", count = 0 },
new TestClass() { name = "123", count = 5 },
new TestClass() { name = "1234", count = 20 },
};
var merged =
list2
.Concat(list1.Select(x => new TestClass() { name = x, count = 0 }))
.GroupBy(x => x.name)
.SelectMany(x => x.Take(1))
.ToList();
It gives me:
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
List<TestClass> lst1 = new List<TestClass>();
lst1.Add(new TestClass(){name="One", count = 1});
lst1.Add(new TestClass(){name="Two", count = 2});
lst1.Add(new TestClass(){name="Three", count = 3});
List<TestClass> lst2 = new List<TestClass>();
lst2.Add(new TestClass(){name="Four", count = 4});
lst2.Add(new TestClass(){name="Two", count = 2});
lst2.Add(new TestClass(){name="Three", count = 3});
var unionlst = lst1.Union(lst2, new TestClassComparer ());
foreach(var x in unionlst){
Console.WriteLine(x.name + ","+x.count);
}
}
class TestClassComparer : IEqualityComparer<TestClass>
{
public bool Equals(TestClass p1, TestClass p2)
{
return p1.name == p2.name && p1.count == p2.count;
}
public int GetHashCode(TestClass p)
{
return p.count;
}
}
public class TestClass {
public string name{ get; set; }
public int count{ get; set; }
}
}
Sample output:
One,1
Two,2
Three,3
Four,4

Cartesian Product of Anonymous type

I am working on code which will give Cartesian product of two anonymous types. These 2 anonymous types are generated from database.
Code for 1st anonymous type:
private IEnumerable<object> GetItem()
{
return _unitOfWork.GetRepository<Item>()
.ListAll()
.Select(x => new
{
itemId = x.Id,
itemName = x.Name
})
}
Code for 2nd anonymous type:
private IEnumerable<object> GetVenue()
{
return _unitOfWork.GetRepository<Venue>()
.ListAll()
.Select(x => new
{
locationName = x.Address.City,
venueId = x.VenueId,
venueName = x.Name
})
}
I have following method to get the data and perform Cartesian product and return the data.
public object GetRestrictLookupInfo(IEnumerable<int> lookupCombinations)
{
IEnumerable<object> restrictList = new List<object>();
if (lookupCombinations.Contains(1))
{
var tempProductProfileList = GetItem();
restrictList = tempProductProfileList.AsEnumerable();
}
if (lookupCombinations.Contains(2))
{
var tempProductGroupList = GetVenue();
restrictList = (from a in restrictList.AsEnumerable()
from b in tempProductGroupList.AsEnumerable()
select new { a, b });
}
return restrictList;
}
I have controller which calls this method and return data in json format.
Controller Code
public HttpResponseMessage GetData(IEnumerable<int> lookupCombinations)
{
var lookupRestrictInfo = _sellerService.GetRestrictLookupInfo(lookupCombinations);
return Request.CreateResponse(HttpStatusCode.OK, lookupRestrictInfo);
}
Response expected is:-
[ {
"itemId": 1,
"itemName": "Music",
"locationName": "Paris",
"venueId": 99,
"venueName": "Royal Festival Hall"
} ]
Response which I receive is
[ {
"a": {
"itemId": 1,
"itemName": "Music"
},
"b": {
"locationName": "Paris",
"venueId": 99,
"venueName": "Royal Festival Hall" } }]
I am not able to get the expected JSON string.
You should start with the simplest possible code that shows your problem; your code above has a lot of complexities that may (or may not) have anything to do with your problem. Is this about manipulating anonymous types? Doing a Cartesian product with LINQ? Converting an object to JSON?
Here's one possible answer to what you might be looking for; notice that you can pass around anonymous types using generics instead of object.
namespace AnonymousTypes
{
class Program
{
static string Serialize(object o)
{
var d = (dynamic)o;
return d.ItemId.ToString() + d.ItemName + d.VenueId.ToString() + d.LocationName + d.VenueName;
}
static string GetData<T>(IEnumerable<T> result)
{
var retval = new StringBuilder();
foreach (var r in result)
retval.Append(Serialize(r));
return retval.ToString();
}
static string GetRestrictLookupInfo()
{
var restrictList = new[] { new { Id = 1, Name = "Music" }, new { Id = 2, Name = "TV" } };
var tempProductGroupList = new[] { new { LocationName = "Paris", Id = 99, Name = "Royal Festival Hall" } };
var result = from item in restrictList
from venue in tempProductGroupList
select new
{
ItemId = item.Id,
ItemName = item.Name,
LocationName = venue.LocationName,
VenueId = venue.Id,
VenueName = venue.Name
};
return GetData(result);
}
public static string GetData()
{
return GetRestrictLookupInfo();
}
static void Main(string[] args)
{
var result = GetData();
}
}
}
If that's not what you're looking for, you might start with code that doesn't use anonymous types, such as
namespace AnonymousTypes
{
sealed class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
sealed class Venue
{
public string LocationName { get; set; }
public int Id { get; set; }
public string Name { get; set; }
}
sealed class ItemAndVenue
{
public int ItemId { get; set; }
public string ItemName { get; set; }
public string LocationName { get; set; }
public int VenueId { get; set; }
public string VenueName { get; set; }
}
class Program
{
static IEnumerable<Item> GetItem()
{
return new[] { new Item { Id = 1, Name = "Music" } };
}
static IEnumerable<Venue> GetVenue()
{
return new[] { new Venue { LocationName = "Paris", Id = 99, Name = "Royal Festival Hall" } };
}
static IEnumerable<ItemAndVenue> GetRestrictLookupInfo()
{
var restrictList = GetItem();
var tempProductGroupList = GetVenue();
var result = from item in restrictList
from venue in tempProductGroupList
select new ItemAndVenue
{
ItemId = item.Id,
ItemName = item.Name,
LocationName = venue.LocationName,
VenueId = venue.Id,
VenueName = venue.Name
};
return result;
}
static string GetData()
{
var v = GetRestrictLookupInfo().First();
return v.ItemId.ToString() + v.ItemName + v.VenueId.ToString() + v.LocationName + v.VenueName;
}
static void Main(string[] args)
{
var result = GetData();
}
}
}
In order to produce a single item in the output you need to create a new type, named or anonymous. Since you are using objects rather than actual types, the quickest approach is to cast them to dynamic:
var tempProductGroupList = GetVenue();
restrictList = (from a in restrictList.Cast<dynamic>()
from b in tempProductGroupList.Cast<dynamic>()
select new {
itemId = (int)a.itemId,
itemName = (string)a.itemName,
locationName = (string)b.locationName,
venueId = (int)b.venueId,
venueName = (string)b.venueName
});
This code is tightly coupled to the code producing both lists, because it assumes the knowledge of the field names of types passed into it dynamically. Any change in the structure of source data must be followed by a change in the code making combinations. In addition, it defeats run-time checking, so you need to be very careful with this code.
Try to create a simple object instead of nesting:
select new { a.itemId, a.itemName, b.locationName }
Like an option:
public object GetRestrictLookupInfo(IEnumerable<int> lookupCombinations)
{
List<Dictionary<string, object>> result = new List<Dictionary<string, object>>();
if (lookupCombinations.Contains(1))
{
var tmp = _unitOfWork.GetRepository<Item>()
.ListAll()
.Select(x => new
{
itemId = x.Id,
itemName = x.Name
})
.Select(x =>
{
var dic = new Dictionary<string, object>();
dic.Add(nameof(x.itemId), x.itemId);
dic.Add(nameof(x.itemName), x.itemName);
return dic;
});
result.AddRange(tmp);
}
if (lookupCombinations.Contains(2))
{
var tmp = _unitOfWork.GetRepository<Venue>()
.ListAll()
.Select(x => new
{
locationName = x.Address.City,
venueId = x.VenueId,
venueName = x.Name
})
.Select(x =>
{
var dic = new Dictionary<string, object>();
dic.Add(nameof(x.locationName), x.locationName);
dic.Add(nameof(x.venueId), x.venueId);
dic.Add(nameof(x.venueName), x.venueName);
return dic;
});
result = result.SelectMany(r => tmp.Select(t => r.Concat(t)));
}
return result;
}
It looks like some magic. I uses dictionary instead of object. It can be make in more clear way (extract few methods), but the idea should be clear.
Then, during serialization it will be presented as you need.

Linq return parent objects that have child items matching ALL items in separate list

I have an object which has a variable length list of items (incomingList in code example) and a list of people which each have a list of items. I want to return only those people that have all the items in the incomingList.
So looking at the example, I only want person 1 and 3 returned.
The people are in a database and I want to retrieve as little data as possible so I am trying to work out what the linq query needs to be to achieve this? If I knew the length of the incomingList was always going to be the same I could do "...Any(..) && ...Any(...) &&" etc - but the length will vary.
void Main()
{
var incomingList = new IncomingItem();
var matchItem1 = new MatchItem { ItemType = "objectId", ItemValue = "60" };
var matchItem2 = new MatchItem { ItemType = "area", ItemValue = "CU" };
incomingList.MatchList = new List<MatchItem>();
incomingList.MatchList.Add(matchItem1);
incomingList.MatchList.Add(matchItem2);
var people = new List<Person>();
var person1 = new Person { Id = 1 };
person1.ListOfItems = new List<Item>();
person1.ListOfItems.Add(new Item { ItemType = "objectId", ItemValue = "60" });
person1.ListOfItems.Add(new Item { ItemType = "objectId", ItemValue = "1" });
person1.ListOfItems.Add(new Item { ItemType = "objectId", ItemValue = "30" });
person1.ListOfItems.Add(new Item { ItemType = "area", ItemValue = "CO" });
person1.ListOfItems.Add(new Item { ItemType = "area", ItemValue = "CU" });
people.Add(person1);
var person2 = new Person { Id = 2 };
person2.ListOfItems = new List<Item>();
person2.ListOfItems.Add(new Item { ItemType = "objectId", ItemValue = "60" });
people.Add(person2);
var person3 = new Person { Id = 3 };
person3.ListOfItems = new List<Item>();
person3.ListOfItems.Add(new Item { ItemType = "objectId", ItemValue = "60" });
person3.ListOfItems.Add(new Item { ItemType = "area", ItemValue = "CU" });
people.Add(person3);
var person4 = new Person { Id = 4 };
person4.ListOfItems = new List<Item>();
person4.ListOfItems.Add(new Item { ItemType = "objectId", ItemValue = "12" });
people.Add(person4);
}
public class IncomingItem
{
public IList<MatchItem> MatchList { get; set; }
}
public class MatchItem
{
public List<object> SomeMoreInformation { get; set; }
public string ItemType { get; set; }
public string ItemValue { get; set; }
}
public class Person
{
public int Id { get; set; }
public IList<Item> ListOfItems { get; set; }
}
public class Item
{
public int Id { get; set; }
public int PersonId { get; set; }
public string ItemType { get; set; }
public string ItemValue { get; set; }
}
This returns all people that have all items of incomingList in their ListOfItems:
var result = people.Where(p => incomingList.MatchList
.All(l => p.ListOfItems.Select(loi => new { loi.ItemType, loi.ItemValue })
.Contains(new { l.ItemType, l.ItemValue }) ));
Anonymous types should have properties with equal names and types to resolve to "equal", which condition is met in this case.

generate all possible combinations for a dynamic list of attributes with their values

I struggle with generating all possible combinations for a List of Attributes with their possible values. What I would like to implement is a Method like this:
public List<Variant> generateAllPossibleVariants(List<Attribute> attributes)
The Attribute Class looks the following:
public class Attribute {
public String Name { get; set; }
public ICollection<String> PossibleValues { get; protected set; }
}
So imagine you have a list of 2 Attributes (while the count is dynamic) with their possible values:
attributeColor with possible Values of ( "red", "blue" )
attributeSize with possible values of ("XL", "L" )
Now my method should return a List of Variant while the Variant Class looks the following:
public class Variant
{
public IDictionary<Attribute, string> AttributeValues { get; private set; }
}
Now my method should return a List of all combinations like the following:
List<Variant> :
Variant.AttributeValues { attributeColor => "red", attributeSize => "XL" }
Variant.AttributeValues { attributeColor => "red", attributeSize => "L" }
Variant.AttributeValues { attributeColor => "blue", attributeSize => "XL" }
Variant.AttributeValues { attributeColor => "blue", attributeSize => "L" }
This is not an optimized code, but you can get the idea and clean it up yourself:
public void Main()
{
var attr1 = new MyAttribute { Name = "Colors", PossibleValues = new List<string> { "red", "blue" } };
var attr2 = new MyAttribute { Name = "Sizes", PossibleValues = new List<string> { "XL", "L" } };
var attr3 = new MyAttribute { Name = "Shapes", PossibleValues = new List<string> { "qube", "circle" } };
var attrList = new List<MyAttribute> { attr1, attr2, attr3 };
var result = attrList.Skip(1).Aggregate<MyAttribute, List<Variant>>(
new List<Variant>(attrList[0].PossibleValues.Select(s => new Variant { AttributeValues = new Dictionary<MyAttribute, string> { {attrList[0], s} } })),
(acc, atr) =>
{
var aggregateResult = new List<Variant>();
foreach (var createdVariant in acc)
{
foreach (var possibleValue in atr.PossibleValues)
{
var newVariant = new Variant { AttributeValues = new Dictionary<MyAttribute, string>(createdVariant.AttributeValues) };
newVariant.AttributeValues[atr] = possibleValue;
aggregateResult.Add(newVariant);
}
}
return aggregateResult;
});
}
public class MyAttribute
{
public string Name { get; set; }
public ICollection<string> PossibleValues { get; set; }
}
public class Variant
{
public IDictionary<MyAttribute, string> AttributeValues { get; set; }
}
You are looking for cartesian product (with dynamic dimension).
One simple way to achieve it is using recursion on the dimension, and each time invoke cartesian product on the result of the recursion, and one of the dimensions.
Pseudo code:
genAllPossibilities(list attributes) //each element in attributes is a list
if length(attributes) == 1:
return attributes[0] //the only element, which is a list of one attribute
else:
curr = head(attributes) // first element in the list
reminder = tails(attributes) // a list of all elements except the first
return cartesianProduct(genAllPossibilities(reminder), curr)
cartesianProduct(list1, list2):
l = new list
for each x1 in list1:
for each x2 in list2:
l.append(new MyObject(x1,x2))
return l

Categories

Resources