I need to send two fields with the same Id in Altair(GraphQl).
mutation{
createGoodsOrder(goodsorder: {
deliveryDate: "2019-10-10"
goodsOrderItems: [
{ orderItemId: 54 quantity: 1 costPerUnit: 1 goodType: INGREDIENT }
{ orderItemId: 54 quantity: 2 costPerUnit: 2 goodType: INGREDIENT }
# { orderItemId: 58 quantity: 2 costPerUnit: 2 goodType: INGREDIENT }
]
}){
id
}
}
When I execute mutation, model contains both fields with the same Id but when I make Fetch, it returns only the first one. If It is not the same, Fetch returns both fields. How can I get both fields with the same Id?
var orderIngredients = _repository.Fetch<Ingredient>(e => model.GoodsOrderItems.Any(g => g.OrderItemId == e.Id)).ToList();
var orderIngredients = _repository.Fetch<Ingredient>(
e => e.IngredientType.PlaceId == model.PlaceId
&& model.GoodsOrderItems.Any(g => g.OrderItemId == e.Id && g.GoodType == GoodsTypes.Ingredient))
.Select(e => new GoodsOrderIngredientCreateModel
{
IngredientId = e.Id,
Quantity = model.GoodsOrderItems.First(i => i.OrderItemId == e.Id).Quantity,
CostPerUnit = model.GoodsOrderItems.First(i => i.OrderItemId == e.Id).CostPerUnit,
TotalPrice = model.GoodsOrderItems.First(i => i.OrderItemId == e.Id).Quantity *
model.GoodsOrderItems.First(i => i.OrderItemId == e.Id).CostPerUnit,
GoodType = GoodsTypes.Ingredient
}).Select(v => new GoodsOrderIngredient
{
Id = v.Id,
IngredientId = v.IngredientId,
Quantity = v.Quantity,
CostPerUnit = v.CostPerUnit,
TotalPrice = v.TotalPrice
}).ToList();
Mutation:
mutation.Field<GoodsOrderType>(
name: "createGoodsOrder",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<GoodsOrderCreateInput>> { Name = nameof(GoodsOrder).ToLower() }
),
resolve: context =>
{
if (context.UserContext is GraphQLUserScopedContext userContext)
{
var goodsOrderService = userContext.ServiceScope.ServiceProvider.GetRequiredService<IVendorService>();
var model = context.GetArgument<GoodsOrderCreateModel>(nameof(GoodsOrder).ToLower());
model.PlaceId = userContext.User.PlaceId;
model.NetworkId = userContext.User.NetworkId;
var goodsOrder = goodsOrderService.CreateGoodsOrder(model);
return goodsOrder;
}
else
throw new ExecutionError(Constants.ErrorCodes.WrongUserContext);
}).RequireAuthorization(PermissionsRequirement
.CreateForPermissionSetAll(
new Dictionary<NetworkPermissions, PermissionLevels>
{ {NetworkPermissions.ERP_Cumulative, PermissionLevels.EditCreate} }));
I don't know c# but probably you don't need intermediate types
var orderIngredients = _repository.Fetch<Ingredient>(
e => e.IngredientType.PlaceId == model.PlaceId
&& model.GoodsOrderItems.Any(g => g.OrderItemId == e.Id && g.GoodType == GoodsTypes.Ingredient))
.Select(v => new GoodsOrderIngredient
{
Id = v.Id,
IngredientId = v.IngredientId,
Quantity = v.Quantity,
CostPerUnit = v.CostPerUnit,
TotalPrice = v.Quantity * v.CostPerUnit
}).ToList();
PS. If GoodsOrderIngredientCreateModel (for create mutation?) contains TotalPrice then total calculations are already in DB ?
which in the second list im trying to create a relationship, however if it cant a match, how do I ignore and not add an item?
var clientData = File.ReadAllLines(txtClients.Text)
.Skip(1)
.Select(x => x.Split(','))
.Select(x => new Client()
{
ClientTempId = x[0],
Email = x[1],
FirstName = x[2],
LastName = x[3],
AccountId = accountId
});
var orderData = File.ReadAllLines(txtOrders.Text)
.Skip(1)
.Select(x => x.Split(','))
.Select(x => new Order()
{
OrderTempId = x[0],
ClientId = clientData.FirstOrDefault(c=>c.ClientTempId == x[1]).Id ==string.Empty?"Error here!!":x[1],
//How do I handle errors, if client does not exist, or row is in wrong format? dont want to break code just want a list or issues
Name = x[3],
AccountId = accountId
});
You can return null instead and then filter those out:
var orderData = File.ReadAllLines(txtOrders.Text)
.Skip(1)
.Select(x => x.Split(','))
.Select(x =>
{
// do your check here, and return null
if (clientData.FirstOrDefault(c => c.ClientTempId == x[1]) == null)
return null;
// otherwise return the normal Order object
return new Order()
{
OrderTempId = x[0],
ClientId = x[1],
Name = x[3],
AccountId = accountId
};
})
// then filter out null values
.Where(x => x != null);
Once that is covered, as EZI pointed out in the comments, your actual check is quite expensive. You can make it more efficient by turning your clientData into a dictionary:
var clientDataDictionary = clientData.ToDictionary(c => c.ClientTempId);
Then, you can do the lookup above in constant time:
if (clientDataDictionary.ContainsKey(x[1]))
return null;
I have a list of Person objects:
List<PersonData> AllPersons
From this list I want all those person objects that are duplicated based on a certain property.
Example, this code give all the duplicates based on the Id
var duplicateKeys = AllPersons.GroupBy(p => p.Id).Select(g => new { g.Key, Count = g.Count() }).Where(x => x.Count > 1).ToList().Select(d => d.Key);
duplicates = AllPersons.Where(p => duplicateKeys.Contains(p.Id)).ToList();
Can the part p.Id be dynamic?
Meaning if the user specifies the unique column in a config file and it's read like so:
string uniqueColumn = "FirstName";
How can the query be composed to add that functionality?
Regards.
You can use Reflection to achieve that:
List<PersonData> AllPersons = new List<PersonData>()
{
new PersonData { Id = 1, FirstName = "Tom" },
new PersonData { Id = 2, FirstName = "Jon" },
new PersonData { Id = 3, FirstName = "Tom" }
};
string uniqueColumn = "FirstName";
var prop = typeof(PersonData).GetProperty(uniqueColumn);
var duplicateKeys = AllPersons.GroupBy(p => prop.GetValue(p, null))
.Select(g => new { g.Key, Count = g.Count() })
.Where(x => x.Count > 1)
.Select(d => d.Key)
.ToList();
var duplicates = AllPersons.Where(p => duplicateKeys.Contains(prop.GetValue(p, null))).ToList();
duplicates have 2 elements with FirstName == "Tom" after query execution.
You might want to look into Dynamic LINQ or PredicateBuilder.
I have a class Project as
public class Project
{ public int ProjectId { get; set; }
public string ProjectName { get; set; }
public string Customer { get; set; }
public string Address{ get; set; }
}
and I have 3 lists
List<Project> lst1; List<Project> lst2; List<Project> lst3;
lst1 contains Person objects with ProjectId and ProjectName.
ProjectId =1, ProjectName = "X", Customer = null, Address = null
ProjectId =2, ProjectName = "Y", Customer = null, Address = null
lst2 contains Person objects with ProjectId and Customer
ProjectId =1,ProjectName = null, Customer = "c1", Address = null
ProjectId =2,ProjectName = null, Customer = "c2", Address = null
, and
lst3 contains Person objects with ProjectId and Address
ProjectId = 1, ProjectName = null, Customer =null, Address = "a1"
ProjectId = 2, ProjectName = null, Customer =null, Address = "a2".
Considering there are multiple such records in each list and ProjectId is Uniqe for each project, How can I merge/combine these list to get one list with merged objects
ProjectId=1, ProjectName="X", Customer="c1", address="a1"
ProjectId=2, ProjectName="Y", Customer="c2", address="a2"
I found thse links similar and tried with it but could not meet the results
Create a list from two object lists with linq
How to merge two lists using LINQ?
Thank You.
This could be done in a multi-step approach pretty simply. First, define a Func<Project, Project, Project> to handle the actual record merging. That is, you are defining a method with a signature equivalent to public Project SomeMethod(Project p1, Project p2). This method implements the merging logic you outlined above. Next, we concatenate the elements of the lists together before grouping them by ProjectId, using our merge delegate as the an aggregate function in the overload of GroupBy which accepts a result selector:
Func<Project, Project, Project> mergeFunc = (p1,p2) => new Project
{
ProjectId = p1.ProjectId,
ProjectName = p1.ProjectName == null ? p2.ProjectName : p1.ProjectName,
Customer = p1.Customer == null ? p2.Customer : p1.Customer,
Address = p1.Address == null ? p2.Address : p1.Address
};
var output = lst1.Concat(lst2).Concat(lst3)
.GroupBy(x => x.ProjectId, (k, g) => g.Aggregate(mergeFunc));
Here's a quick and dirty test of the above logic along with output:
List<Project> lst1; List<Project> lst2; List<Project> lst3;
lst1 = new List<Project>
{
new Project { ProjectId = 1, ProjectName = "P1" },
new Project { ProjectId = 2, ProjectName = "P2" },
new Project { ProjectId = 3, ProjectName = "P3" }
};
lst2 = new List<Project>
{
new Project { ProjectId = 1, Customer = "Cust1"},
new Project { ProjectId = 2, Customer = "Cust2"},
new Project { ProjectId = 3, Customer = "Cust3"}
};
lst3 = new List<Project>
{
new Project { ProjectId = 1, Address = "Add1"},
new Project { ProjectId = 2, Address = "Add2"},
new Project { ProjectId = 3, Address = "Add3"}
};
Func<Project, Project, Project> mergeFunc = (p1,p2) => new Project
{
ProjectId = p1.ProjectId,
ProjectName = p1.ProjectName == null ? p2.ProjectName : p1.ProjectName,
Customer = p1.Customer == null ? p2.Customer : p1.Customer,
Address = p1.Address == null ? p2.Address : p1.Address
};
var output = lst1
.Concat(lst2)
.Concat(lst3)
.GroupBy(x => x.ProjectId, (k, g) => g.Aggregate(mergeFunc));
IEnumerable<bool> assertedCollection = output.Select((x, i) =>
x.ProjectId == (i + 1)
&& x.ProjectName == "P" + (i+1)
&& x.Customer == "Cust" + (i+1)
&& x.Address == "Add" + (i+1));
Debug.Assert(output.Count() == 3);
Debug.Assert(assertedCollection.All(x => x == true));
--- output ---
IEnumerable<Project> (3 items)
ProjectId ProjectName Customer Address
1 P1 Cust1 Add1
2 P2 Cust2 Add2
3 P3 Cust3 Add3
Using a Lookup you can do it like this:
List<Project> lst = lst1.Union(lst2).Union(lst3).ToLookup(x => x.ProjectId).Select(x => new Project()
{
ProjectId = x.Key,
ProjectName = x.Select(y => y.ProjectName).Aggregate((z1,z2) => z1 ?? z2),
Customer = x.Select(y => y.Customer).Aggregate((z1, z2) => z1 ?? z2),
Address = x.Select(y => y.Address).Aggregate((z1, z2) => z1 ?? z2)
}).ToList();
I belive the folloing is how LINQ Join works:
var mergedProjects =
lst1
.Join(lst2,
proj1 => proj1.ProjectID,
proj2 => proj2.ProjectID,
(proj1, proj2) => new { Proj1 = proj1, Proj2 = proj2 })
.Join(lst3,
pair => pair.Proj1.ProjectID,
proj3 => proj3.ProjectID,
(pair, proj3) => new Project
{
ProjectID = proj3.ProjectID,
ProjectName = pair.Proj1.ProjectName,
Customer = pair.Proj2.Customer,
Address = proj3.Address
});
This will not return any results where the ProjectID is not found in all three lists.
If this is a problem, I think you'd be better off doing this manually rather than using LINQ.
I assume that list contains same number of items and are sorted by ProjectId.
List<Project> lst1; List<Project> lst2; List<Project> lst3
If list are not sorted you can sort it first.
list1.Sort(p => p.ProjectId);
list2.Sort(p => p.ProjectId);
list3.Sort(p => p.ProjectId);
For merging the object
List<Project> list4 = new List<Project>();
for(int i=1; i<list.Count; i++)
{
list4.Add(new Project
{
ProjectId = list1[i].ProjectId;
ProjectName = list1[i].ProjectName;
Customer = list2[i].Customer;
Address = list3[i].Address;
});
}
Although overkill, I was tempted to make this an extension method:
public static List<T> MergeWith<T,TKey>(this List<T> list, List<T> other, Func<T,TKey> keySelector, Func<T,T,T> merge)
{
var newList = new List<T>();
foreach(var item in list)
{
var otherItem = other.SingleOrDefault((i) => keySelector(i).Equals(keySelector(item)));
if(otherItem != null)
{
newList.Add(merge(item,otherItem));
}
}
return newList;
}
Usage would then be:
var merged = list1
.MergeWith(list2, i => i.ProjectId,
(lhs,rhs) => new Project{ProjectId=lhs.ProjectId,ProjectName=lhs.ProjectName, Customer=rhs.Customer})
.MergeWith(list3,i => i.ProjectId,
(lhs,rhs) => new Project{ProjectId=lhs.ProjectId,ProjectName=lhs.ProjectName, Customer=lhs.Customer,Address=rhs.Address});
Live example: http://rextester.com/ETIVB14254
This is assuming that you want to take the first non-null value, or revert to the default value - in this case null for a string.
private static IEnumerable<Project> GetMergedProjects(IEnumerable<List<Project>> projects)
{
var projectGrouping = projects.SelectMany(p => p).GroupBy(p => p.ProjectId);
foreach (var projectGroup in projectGrouping)
{
yield return new Project
{
ProjectId = projectGroup.Key,
ProjectName =
projectGroup.Select(p => p.ProjectName).FirstOrDefault(
p => !string.IsNullOrEmpty(p)),
Customer =
projectGroup.Select(c => c.Customer).FirstOrDefault(
c => !string.IsNullOrEmpty(c)),
Address =
projectGroup.Select(a => a.Address).FirstOrDefault(
a => !string.IsNullOrEmpty(a)),
};
}
}
You could also make this an extension method if needed.
Hello I have a little problem with assigning property values from one lists items to anothers. I know i could solve it "the old way" by iterating through both lists etc. but I am looking for more elegant solution using LINQ.
Let's start with the code ...
class SourceType
{
public int Id;
public string Name;
// other properties
}
class DestinationType
{
public int Id;
public string Name;
// other properties
}
List<SourceType> sourceList = new List<SourceType>();
sourceList.Add(new SourceType { Id = 1, Name = "1111" });
sourceList.Add(new SourceType { Id = 2, Name = "2222" });
sourceList.Add(new SourceType { Id = 3, Name = "3333" });
sourceList.Add(new SourceType { Id = 5, Name = "5555" });
List<DestinationType> destinationList = new List<DestinationType>();
destinationList.Add(new DestinationType { Id = 1, Name = null });
destinationList.Add(new DestinationType { Id = 2, Name = null });
destinationList.Add(new DestinationType { Id = 3, Name = null });
destinationList.Add(new DestinationType { Id = 4, Name = null });
I would like to achieve the following:
destinationList should be filled with Names of corresponding entries (by Id) in sourceList
destinationList should not contain entries that are not present in both lists at once (eg. Id: 4,5 should be eliminated) - something like inner join
I would like to avoid creating new destinationList with updated entries because both lists already exist and are very large,
so no "convert" or "select new".
In the end destinationList should contain:
1 "1111"
2 "2222"
3 "3333"
Is there some kind of elegant (one line Lambda? ;) solution to this using LINQ ?
Any help will be greatly appreciated! Thanks!
I would just build up a dictionary and use that:
Dictionary<int, string> map = sourceList.ToDictionary(x => x.Id, x => x.Name);
foreach (var item in destinationList)
if (map.ContainsKey(item.Id))
item.Name = map[item.Id];
destinationList.RemoveAll(x=> x.Name == null);
Hope this will your desired result. First join two list based on key(Id) and then set property value from sourceList.
var result = destinationList.Join(sourceList, d => d.Id, s => s.Id, (d, s) =>
{
d.Name = s.Name;
return d;
}).ToList();
Barring the last requirement of "avoid creating new destinationList" this should work
var newList = destinationList.Join(sourceList, d => d.Id, s => s.Id, (d, s) => s);
To take care of "avoid creating new destinationList", below can be used, which is not any different than looping thru whole list, except that it probably is less verbose.
destinationList.ForEach(d => {
var si = sourceList
.Where(s => s.Id == d.Id)
.FirstOrDefault();
d.Name = si != null ? si.Name : "";
});
destinationList.RemoveAll(d => string.IsNullOrEmpty(d.Name));
Frankly, this is the simplest:
var dictionary = sourceList.ToDictionary(x => x.Id, x => x.Name);
foreach(var item in desitnationList) {
if(dictionary.ContainsKey(item.Id)) {
item.Name = dictionary[item.Id];
}
}
destinationList = destinationList.Where(x => x.Name != null).ToList();
You could do something ugly with Join but I wouldn't bother.
I hope this will be useful for you. At the end, destinationList has the correct data, without creating any new list of any kind.
destinationList.ForEach(x =>
{
SourceType newSource = sourceList.Find(s=>s.Id == x.Id);
if (newSource == null)
{
destinationList.Remove(destinationList.Find(d => d.Id == x.Id));
}
else
{
x.Name = newSource.Name;
}
});