How can I add list parameters to a LINQ query? - c#

I have two parameters passed to the method and I need to attach them to the final query list.
(1st parameter)
string[] Price= new string[5];
Price= new string[] { "50", "25", "35" };
(2nd parameter)
List<string> DiscountPrice= new List<string>();
DiscountPrice.Add ("10");
DiscountPrice.Add ("5");
DiscountPrice.Add ("3");
var list= (from d in context.List
where ....
select new MyNewList
{
Name = d.Name,
Country = d.Country,
**Price = ??** //how do I attach the parameters one by one? In the order they were saved?
**DiscountPrice** = ??
}).ToList<MyNewList>();

It sounds like you want to match list elements by index. You can iterate from zero to the number of list elements and access each element by its index:
var prices = new string[] { "50", "25", "35" };
var discountPrices = new List<string>() { "10", "5", "3" };
var items = (from d in context.List
where ....
select new { d.Name, d.Country }).ToList();
var list = (from index in Enumerable.Range(0, items.Count())
select new MyNewList
{
Name = items[index].Name,
Country = items[index].Country,
Price = prices[index],
DiscountPrice = discountPrices[index]
}).ToList();
Another way is to Zip everything together:
var list = items.Zip(prices, (item, price) => new { item, price })
.Zip(discountPrices, (x, discountPrice) => new { x.item, x.price, discountPrice})
.Select(x => new MyNewList
{
Name = x.item.Name,
Country = x.item.Country,
Price = x.price,
DiscountPrice = x.discountPrice
})
.ToList();

Related

LINQ merging 2 lists, keeping seqeunce and origin [duplicate]

This question already has answers here:
LINQ - Full Outer Join
(16 answers)
Closed 4 years ago.
Here I have 2 lists of same object type.
object = {id: xxx, ...} // attribute "id" is used to find the identical obj
List oldSet = [old1, old2, old3];
List newSet = [new2, new3, new4];
// old2 = {id= 2, result = 5, ...}
// new2 = {id= 2, result = 1, ...}
// expected result = {oldSet: old2; newSet: new2}
I want to merge both lists, also keeping the origin of which list it came from.
The expected result as below:
List mergedSet = [{old1, null}, {old2, new2}, {old3, new3}, {null, new4}];
I'm thinking to use LINQ C# for it, but stuck somewhere.
Kindly advise.
Thanks! :)
Here's some code that does what you want using Linq. It basically walks through all the old list, and adds pairs to the merged list by looking for matches from the new list (and adding null as the second item if no match was found). Then it walks through the remaining items in the new list and adds them with null for the first item. It selects a dynamic type with two properties: OldSet and NewSet, so you know where each item came from.
The merge code is simply:
var mergedSet = oldSet.Select(o =>
new {OldSet = o, NewSet = newSet.FirstOrDefault(n => n.id == o.id)})
.Concat(newSet.Where(n => oldSet.All(o => o.id != n.id)).Select(n =>
new {OldSet = (Item) null, NewSet = n}));
This is based on the following item class:
class Item
{
public int id { get; set; }
public string result { get; set; }
public override string ToString()
{
return $"{result}{id}";
}
}
We create our lists:
List<Item> oldSet = new List<Item>
{
new Item {id = 1, result = "old"},
new Item {id = 2, result = "old"},
new Item {id = 3, result = "old"},
};
List<Item> newSet = new List<Item>
{
new Item {id = 2, result = "new"},
new Item {id = 3, result = "new"},
new Item {id = 4, result = "new"},
};
Run the merge code (very first snippet), and then display results:
foreach (var item in mergedSet)
{
Console.WriteLine($"{item.NewSet},{item.OldSet}");
}
Output
Try something like this :
List<string> oldSet = new List<string>() {"old1", "old2", "old3"};
List<string> newSet = new List<string>() {"new2", "new3", "new4"};
var results = oldSet.Select((x,i) => new { oldSet = x, newSet = newSet[i]}).ToList();
You can left join the two lists. I edited the answer as you actually need to left join twice, union, and apply a select distinct to get the cases where oldSet = null and no duplicates...
var mergedSet = (from o in oldSet
join n in newSet on o.id equals n.id into ns
from n in ns.DefaultIfEmpty()
select new { OldSet = o, NewSet = n })
.Union(from n in newSet
join o in oldSet on n.id equals o.id into os
from o in os.DefaultIfEmpty()
select new { OldSet = o, NewSet = n })
.Distinct();
Might be an overkill, but if you really want to use LINQ
List<Item> oldSet = new List<Item>
{
new Item {id = 1, result = "old"},
new Item {id = 2, result = "old"},
new Item {id = 3, result = "old"},
};
List<Item> newSet = new List<Item>
{
new Item {id = 2, result = "new"},
new Item {id = 3, result = "new"},
new Item {id = 4, result = "new"},
};
var resultL = oldSet.GroupJoin(
newSet,
o => o.id,
n => n.id,
(o,n) => new { Old = o, New = n })
.SelectMany(
n => n.New.DefaultIfEmpty(),
(o,n) => new Tuple<Item,Item>(o.Old,n));
var resultR= newSet.GroupJoin(
oldSet,
n => n.id,
o=> o.id,
(n,o) => new { Old = o, New = n })
.SelectMany(
o=> o.Old.DefaultIfEmpty(),
(n,o) => new Tuple<Item,Item>(o,n.New));
var result = resultL.Union(resultR).Distinct();
In this case, you have to use two GroupJoin and the Union the results.
Look at the following code:
var res1 = oldSet.GroupJoin(newSet, o => o, k => k, (x, y) => { var yy = y.FirstOrDefault(); return new { X = x, Y = yy }; });
var res2 = newSet.GroupJoin(oldSet, o => o, k => k, (x, y) => { var yy = y.FirstOrDefault(); return new { X = yy, Y = x }; });
var result = res1.Union(res2).ToList();// Your result is here

How to sort linq with fixed values and show all the rest with another sorting [duplicate]

This question already has answers here:
LINQ OrderBy versus ThenBy
(4 answers)
Closed 5 years ago.
IQueryable<Employee> query = ((IEnumerable<Employee>)employeeList)
.Select(x => x)
.AsQueryable();
var strListEmployees = input.MustIncludeIdsInPage.Split(",").ToList();
//the list of employee is dynamic, it'd return 3, 4, 5 or more data
var entities = query
.OrderBy(item => strListEmployees.IndexOf(item.Id.ToString()))
.PageBy(input)
.ToList();
example data
What I want is something like this in order:
by employee name
D
F
A
B
C
E
G
H
Employee D, F, A on top (fix value in List) and show the rest with name sorting (order by).
As M. Wiśnicki mentioned, this is easily solveable as You got only 3 elements. But to dynamically resolve this, I would stick to some function, where You would enter the List (or IEnumerable) of the objects and also the Names, based on which You want to filter them.
The code below is recursion, which will go through the array and select the 1st element (from array) and add the rest. Rest is calling the same function without the 1st name & without the element we have already added.
Something like:
public IEnumerable<Employee> GetOrderedPrefered(IEnumerable<Employee> aList, string[] aNames)
{
if (aNames.Length == 0) return aList.OrderBy(a => a.Name).ToList();
var lRes = new List<Employee>()
{
aList.FirstOrDefault(a => a.Name == aNames[0])
};
lRes.AddRange(
GetOrderedPrefered(
aList.Where(a => a.Name != aNames[0]),
aNames.Where(a => a != aNames.First()
).ToArray()
));
return lRes;
}
Usage:
var lRes = GetOrderedPrefered(persons, names);
foreach (var item in lRes)
Console.WriteLine(item.Name);
> D
> F
> A
> B
> C
> E
> G
You can use OrderBy() and ThenBy()
List<Test> tests = new List<Test>()
{
new Test() {EmployeeID = "1", Name = "A"},
new Test() {EmployeeID = "2", Name = "B"},
new Test() {EmployeeID = "3", Name = "C"},
new Test() {EmployeeID = "4", Name = "D"},
new Test() {EmployeeID = "5", Name = "E"},
new Test() {EmployeeID = "6", Name = "F"},
new Test() {EmployeeID = "7", Name = "G"},
new Test() {EmployeeID = "8", Name = "H"},
};
var x = tests.OrderBy(name => name.Name != "D")
.ThenBy(name => name.Name != "F")
.ThenBy(name => name.Name != "A")
.ThenBy(name => name.Name)
.ToList();
Result is: First D,F,A and others names
Edit:
string[] filtr = new[] {"D", "F", "A"};
var fdata = tests.Where(d => filtr.Contains(d.Name)).OrderBy(z=>z.Name).ToList();
var odata = tests.Where(d => !filtr.Contains(d.Name)).OrderBy(z => z.Name).ToList();
fdata.AddRange(odata);
var set = Enumerable.Range(0, 8)
.Select(i => new {
Name = new string(new[] { (char)('A' + i) })
});
var before = string.Join(",", set.Select(i => i.Name)); //A,B,C,D,E,F,G,H
var priorities = "D,F".Split(',').Select((v, i) => new { Value = v, Index = i });
var query = from s in set
join p in priorities on s.Name equals p.Value into m
from x in m.DefaultIfEmpty(new { Value = s.Name, Index = int.MaxValue })
orderby x.Index, s.Name
select s.Name;
var result = string.Join(",", query); //D,F,A,B,C,E,G,H

How to return Distinct Row using LINQ

I have two rows which have all the data same except one column.
I want to show only one row on the UI but one row which has different data should be shown as comma seperated values.
Sample Data
PricingID Name Age Group
1 abc 56 P1
1 abc 56 P2
Output should be :
PricingID Name Age Group
1 abc 56 P1,P2
I am using this approach but it is not working , it gives me two rows only but data i am able to concatenate with comma.
List<PricingDetailExtended> pricingDetailExtendeds = _storedProcedures.GetPricingAssignment(pricingScenarioName, regionCode, productCode, stateCode, UserId, PricingId).ToList();
var pricngtemp = pricingDetailExtendeds.Select(e => new
{
PricingID = e.PricingID,
OpportunityID = e.OpportunityID,
ProductName = e.ProductName,
ProductCD = e.ProductCD
});
pricingDetailExtendeds.ForEach(e=>
{
e.ProductCD = string.Join(",",string.Join(",", (pricngtemp.ToList().Where(p => p.PricingID == e.PricingID).Select(k => k.ProductCD).ToArray())).Split(',').Distinct().ToArray());
e.OpportunityID =string.Join(",", string.Join(",", (pricngtemp.ToList().Where(p => p.PricingID == e.PricingID).Select(k => k.OpportunityID).ToArray())).Split(',').Distinct().ToArray());
e.ProductName =string.Join(",", string.Join(",", (pricngtemp.ToList().Where(p => p.PricingID == e.PricingID).Select(k => k.ProductName).ToArray())).Split(',').Distinct().ToArray());
}
);
// pricingDetailExtendeds = GetUniquePricingList(pricingDetailExtendeds);
return pricingDetailExtendeds.Distinct().AsEnumerable();
Any body can suggest me better approach and how to fix this issue ?
Any help is appreciated.
You want to use the GroupBy linq function.
I then use the String.Join function to make the groups comma seperated.
So something like this:
var pricingDetailExtendeds = new[]
{
new
{
PricingID = 1,
Name = "abc",
Age = 56,
Group = "P1"
},
new
{
PricingID = 1,
Name = "abc",
Age = 56,
Group = "P2"
}
};
var pricngtemp =
pricingDetailExtendeds.GroupBy(pde => new {pde.PricingID, pde.Name, pde.Age})
.Select(g => new {g.Key, TheGroups = String.Join(",", g.Select(s => s.Group))}).ToList();
You can easily extrapolate this to the other fields.
To return the PricingDetailExtended, the just create it in the select. So something like this
.Select(g => new PricingDetailExtended {
PricingID = g.Key.PricingId,
TheGroups = String.Join(",", g.Select(s => s.Group))
}).ToList();
You won't have the field TheGroups though, so just replace that field with the proper one.
An example of what I was describing in my comment would be something along the lines of the following. I would expect this to be moved into a helper function.
List<PriceDetail> list = new List<PriceDetail>
{
new PriceDetail {Id = 1, Age = 56, Name = "abc", group = "P1"},
new PriceDetail {Id = 1, Age = 56, Name = "abc", group = "P2"},
new PriceDetail {Id = 2, Age = 56, Name = "abc", group = "P1"}
};
Dictionary<PriceDetailKey, StringBuilder> group = new Dictionary<PriceDetailKey, StringBuilder>();
for (int i = 0; i < list.Count; ++i)
{
var key = new PriceDetailKey { Id = list[i].Id, Age = list[i].Age, Name = list[i].Name };
if (group.ContainsKey(key))
{
group[key].Append(",");
group[key].Append(list[i].group);
}
else
{
group[key] = new StringBuilder();
group[key].Append(list[i].group);
}
}
List<PriceDetail> retList = new List<PriceDetail>();
foreach (KeyValuePair<PriceDetailKey, StringBuilder> kvp in group)
{
retList.Add(new PriceDetail{Age = kvp.Key.Age, Id = kvp.Key.Id, Name = kvp.Key.Name, group = kvp.Value.ToString()});
}
you could even convert the final loop into a LINQ expression like:
group.Select(kvp => new PriceDetail {Age = kvp.Key.Age, Id = kvp.Key.Id, Name = kvp.Key.Name, group = kvp.Value.ToString()});
Its worth noting you could do something similar without the overhead of constructing new objects if, for example, you wrote a custom equality comparer and used a list instead of dictionary. The upside of that is that when you were finished, it would be your return value without having to do another iteration.
There are several different ways to get the results. You could even do the grouping in SQL.

How to intersect list in c#?

I have following list of Item objects in c#:
public class Item
{
public int Id { get; set; }
public List<string> Orders { get; set; }
}
List<Item> item = new List<Item>() {
new Item() { Id = 1, Code = 23, Orders = new List<string>() { "A", "B" }},
new Item() { Id = 2, Code = 24, Orders = new List<string>() { "C", "D" }},
new Item() { Id = 1, Code = 23, Orders = new List<string>() { "E", "F" }},
new Item() { Id = 3, Code = 25, Orders = new List<string>() { "G", "H" }}
};
I want to concat the Orders whose Id is same, so the output of above list should be:
{
new Item() { Id = 1, Code = 23, Orders = new List<string>() { 'A', 'B', 'E', 'F' },
new Item() { Id = 2, Code = 24, Orders = new List<string>() { 'C', 'D' },
new Item() { Id = 3, Code = 25, Orders = new List<string>() { 'G', 'H' }
};
How can i do this efficiently in c# ( using linq if possible ) ?
You want to group the items based on their ID, and then create a new sequences based on all of the Orders for that group.
var query = items.GroupBy(item => item.Id)
.Select(group => new Item
{
Id = group.Key,
Orders = group.SelectMany(item => item.Orders).ToList()
});
Note that this is not the intersection of any data. You're getting the union of all data within each group.
It appears what you want is something like this:
var output = items.GroupBy(i => i.Id)
.Select(g => new Item()
{
Id = g.Key
Orders = g.SelectMany(i => i.Orders)
.ToList()
});
Or in query syntax:
var output =
from i in items
group i by i.Id into g
select new Item()
{
Id = g.Key
Orders = g.SelectMany(i => i.Orders).ToList()
};
You can group your items by their id, then create new item for each id concatenate the orders:
item.GroupBy(x => x.Id)
.Select(x => new Item
{
Id = x.Key,
Orders = x.SelectMany(a => a.Orders).ToList()
}).ToList();

Descending sorting of a list of object based on a count from a different class

I'm stuck on this problem where I need to do descending sort based on other list. l_lstNames need to update by age descending.
public class Test
{
public String Name;
public Int32 Age;
}
List<String> l_lstNames = new List<String> { "A1", "A3", "A2", "A4", "A0" };
List<Test> l_lstStudents = new List<Test>
{
new Test { Age = 33, Name = "A0" },
new Test { Age = 10, Name = "A1" },
new Test { Age = 50, Name = "A2" },
new Test { Age = 8, Name = "A3" },
new Test { Age = 25, Name = "A4" },
};
// Output
List<String> l_lstNames = new List<String> { "A2", "A0", "A4", "A1", "A3" };
Found few sames samples but not matching what I'm looking for. Thank you for help.
Create Dictionary<string, int> with Name to Age mapping and use it within order method:
var dict = students.ToDictionary(x => x.Name, x => x.Age);
var ordered = source.OrderByDescending(x => dict[x.Name]).ToList();
or you can just order students collection and then select Name only:
var ordered = students.OrderByDescending(x => x.Age)
.Select(x => x.Name)
.ToList();
If you just want the names in order descending:
var sorted = l_lstStudents // From the list of students
.OrderByDescending(l => l.Age) // with the oldest student first
.Select(s => s.Name) // give me just the names
.ToList(); // in a list!
I think this is what you are looking for
List<String> l_lstNames1 = (from student in l_lstStudents
where l_lstNames.Any(a => student.Name == a)
orderby student.Age descending
select student.Name ).ToList();
OR
List<String> l_lstNames2 = l_lstStudents.OrderByDescending(a => a.Age)
.Where(a => l_lstNames.Any(b => b == a.Name))
.Select(a => a.Name).ToList();

Categories