Linq equivalent in lambda - c#

I have this structure of class
Flight
..OriginDestinations
....FlightSegments
My question is prett easy, what can be equivalent in lambda expression:
var result1 = (
from sf in selectedFlights
from odo in sf.OriginDestinationOptions
from fs in odo.FlightSegments
select new FlightNumberAndClass {
FlightNumber = fs.FlightNumber,
FlightClass = fs.FlightClass
});
where result1 is type of IEnumerable<FlightNumberAndClass>
I tried this but:
var result2 =
selectedFlights.Select(
x => x.OriginDestinationOptions.Select(
y =>
y.FlightSegments.Select(
z => new FlightNumberAndClass {
FlightNumber = z.FlightNumber,
FlightClass = z.FlightClass
}
)
)
);
it gives me result2 is type of "something like" System.Linq.Enumerable.WhereSelectListIterator<IEnumerable<IEnumerable<FlightNumberAndClass>>>

SelectMany projects each element of a sequence to an IEnumerable<T> and flattens the resulting sequences into one sequence.
IEnumerable<FlightNumberAndClass> result2 = selectedFlights
.SelectMany(sf => sf.OriginDestinationOptions
.SelectMany(odo => odo.FlightSegments
.Select(fs => new FlightNumberAndClass
{
FlightNumber = fs.FlightNumber,
FlightClass = fs.FlightClass
}
)
)
);

In situation like this you can use SelectMany like this example:
List<List<List<string>>> list1 = new List<List<List<string>>>
{
new List<List<string>> {
new List<string>{"a","b","c","d","e"},
new List<string>{"a","b","c","d","e"},
new List<string>{"a","b","c","d","e"}
},
new List<List<string>> {
new List<string>{"a","b","c","d","e"},
new List<string>{"a","b","c","d","e"},
new List<string>{"a","b","c","d","e"}
},
new List<List<string>> {
new List<string>{"a","b","c","d","e"},
new List<string>{"a","b","c","d","e"},
new List<string>{"a","b","c","d","e"}
}
};
var result = list1.SelectMany(a => a.SelectMany(b => b.Select(c => c))).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

Execute query using LINQ or EF to fetch records from multiple tables

I've been searching for a while now. But all the solutions seems to be different than what I expect.
So this is my query in SQL:-
Select * from
(
select Name,Description Descr from CourseTbl
union all
select MainDesc Name,MainDesc Descr from CoursedescTbl
union all
select SubHeading Name,SubDesc Descr from CourseSubDesc
union all
select Name,Descr as Descr from InternTbl
)A where A.Name like '%D%' or A.Descr like '%D%'
I want to execute the above query using LINQ or EF. and return the list in Json format. So I tried many failed attempts and this is one of them:-
public JsonResult SearchDetail()
{
string SearchKey = Request.Form["SearchName"].ToString();
IEnumerable<SearchList> QueryResult;
using (EBContext db = new EBContext())
{
try
{
QueryResult =
(from x in db.Courses
select new { A = x.Name, B = x.Description })
.Concat(from y in db.CourseDesc
select new { A = y.MainHeading, B = y.MainDesc })
.Concat(from z in db.CourseSubDesc
select new { A = z.SubDesc, B = z.SubHeading })
.Concat(from w in db.Interns
select new { A = w.Name, B = w.Descr })
.ToList();
}
catch (Exception ex)
{
return new JsonResult
{
Data = ex.Message,
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
return new JsonResult
{
Data = QueryResult,
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
}
And my SearchList Class is like this:-
public class SearchList
{
public string Name { get; set; }
public string Descr { get; set; }
}
I'm not able to put the where clause in linq query which will search in all table.
I'm getting error when I assign queryresult to my ef query. It says cannot cast to Innumerable.
Thanks in Advance.
Could you explain more on the error you are getting?
Also, have you tried using .Union() in linq?
QueryResult = db.Courses.Select(x=> new { A = x.Name, B= x.Description})
.Union(db.CourseDesc.Select(y=> new {A = y.MainHeading, B = y.MainDesc })
.Union( //so on
.ToList(); //this isn't necessary
Edit: There are two ways to input where clause, either with each search, or at the end:
QueryResult = db.Courses.Where(x=>x.Name == "Name").Select(x=> new { A = x.Name, B= x.Description})
.Union(db.CourseDesc.Where(y=>y.MainHeading == "Name").Select(y=> new {A = y.MainHeading, B = y.MainDesc })
.Union( //so on
.ToList();
Or:
QueryResult = db.Courses.Where(x=>x.Name == "Name").Select(x=> new { A = x.Name, B= x.Description})
.Union(db.CourseDesc.Where(y=>y.MainHeading == "Name").Select(y=> new {A = y.MainHeading, B = y.MainDesc })
.Union( //so on
//Where can go either before or after .ToList
.Where(item=>item.A == "Name")
.ToList();
You did not say what error/exception you are getting. But your QueryResult is of type IEnumerable<SearchList> and you appear to be assigning it an enumerable of anonymous type { A, B }.
Try this:
QueryResult = (from x in db.Courses
select new SearchList { Name = x.Name, Descr = x.Description })
.Concat(...)
.ToList();
Or
QueryResult = db.Courses.Select(x => new SearchList
{ Name = x.Name, Descr = x.Description})
.Concat(...)
.ToList();
UPDATE
Your #2 issue will be fixed if you changed your select to new up a SearchList as I did above, instead of new-ing an anonymous type.
As for your issue #1, you should insert the Where() before your Select():
result1 = db.Courses
.Where(x => x.Name.Contains('D') || x.Description.Contains('D'))
.Select(x => new SearchList { Name = x.Name, Descr = x.Description});
result2 = db.CourseDesc
.Where(y => y.MainHeading.Contains('D') || y.MainDesc.Contains('D'))
.Select(y => new SearchList { Name = y.MainHeading, Descr = y.MainDesc});
result3 = db.CourseSubDesc
.Where(...)
.Select(...);
QueryResult = result1.Concat(result2).Concat(result3).ToList();
Doing Where() as part of the query on each table is important so you do not fetch all records from that table, unlike if you do the Where() after Concat(). Also note that Concat() may throw an ArgumentNullException.
Take the lists Separately and query and concat
check this example
List<string> a = new List<string>() { "a", "b", "c" };
List<string> b = new List<string>() { "ab", "bb", "cb" };
IEnumerable<SearchList> QueryResult =
a.Where(x => x.Contains("a")).Select(x => new SearchList() { Name = x, Descr = x })
.Concat(b.Where(x => x.Contains("a")).Select(x => new SearchList() { Name = x, Descr = x }));

AggregationContainer vs. AggregationDescriptor

I am trying to send an array of ranges to an Aggregation Descriptor, but the Lambda expression expects a comma delimited expresions
.Aggregations(agg =>
{
AggregationDescriptor ag = agg.Terms("objectTypes", ot => ot.Field("doc.objectType"));
if (!parameters.ContainsKey("userID"))
ag = ag.Terms("users", ot => ot.Field("doc.entryUserID"));//.Field("doc.sourceUserID")))
ag.Terms("commentTypes", ot => ot.Field("doc.commentType"));
if (!parameters.ContainsKey("dateRange"))
{
Dictionary<string, SearchDateRange> dateMap = GetDateRangeMap();
ag.DateRange("dates", dr => dr.Field("doc.date").Format("yyyy-MM-dd")
.Ranges(r1 => r1.Key("Today").From(dateMap["Today"].startDate.Value.ToString("yyyy-MM-dd")).To("now"),
r2 => r2.Key("SinceWednesday").From(dateMap["Today"].startDate.Value.ToString("yyyy-MM-dd")).To("now"),
r3 => r3.Key("ThisYear").From(dateMap["ThisYear"].startDate.Value.ToString("yyyy-MM-dd")).To("now"),
r3 => r3.Key("Last2Years").From(dateMap["Last2Years"].startDate.Value.ToString("yyyy-MM-dd")).To("now"),
r4 => r4.Key("Last3Years").From(dateMap["Last3Years"].startDate.Value.ToString("yyyy-MM-dd")).To("now")
));
}
The above code works.
Below I would like to use the Range[] array and pass it to the aggregate discriptor but I can't, but I can create an AggregationContainer with the range array. How do I marry these two pieces together?
if (!parameters.ContainsKey("revenueRange") && docTypes.Contains(CouchbaseDocumentType.Company))
{
Dictionary<string, SearchNumberRange> numMap = GetMoneyRangeMap();
Range<double>[] ranges = numMap.Select(m =>
{
var r = new Range<double>().Key(m.Key);
if (m.Value.low.HasValue) r.From(m.Value.low.Value);
if (m.Value.high.HasValue) r.To(m.Value.high.Value);
return r;
}).ToArray();
AggregationContainer agr = new AggregationContainer
{
Range = new RangeAggregator { Field = "doc.lastFinancial.revenueUSD", Ranges = ranges }
};
}
return ag;
}
)
I created simple example to show you how you can achieve this.
var funcs = new List<Func<Range<double>, Range<double>>>();
funcs.Add(range => new Range<double>().From(1).To(2));
funcs.Add(range => new Range<double>().From(3).To(4));
var searchResponse = client.Search<Document>(
s => s.Aggregations(agg => agg.Range("range", descriptor => descriptor.Field(f => f.Number).Ranges(funcs.ToArray()))));
Document class:
public class Document
{
public int Id { get; set; }
public double Number { get; set; }
}
Hope you won't have problem with putting it into your context.

Linq how to query a list of items for a combined list of a child collection based on a property of the parent item

Hi say I have the objects:
public class InvoiceLine
{
}
and
public class InvoiceHeader
{
public char Group { get; set; }
public List<InvoiceLine> InvoiceLines { get; set; }
}
Data is set up for them as follows:
var invoiceLine1 = new InvoiceLine();
var invoiceLine2 = new InvoiceLine();
var invoiceLine3 = new InvoiceLine();
var invoiceLine4 = new InvoiceLine();
var invoiceLine5 = new InvoiceLine();
var invoiceLine6 = new InvoiceLine();
var invoiceLine7 = new InvoiceLine();
var invoiceLine8 = new InvoiceLine();
var invoiceHeader1 = new InvoiceHeader { Group = 'A', InvoiceLines = new List<InvoiceLine> { invoiceLine1, invoiceLine2 } };
var invoiceHeader2 = new InvoiceHeader { Group = 'A', InvoiceLines = new List<InvoiceLine> { invoiceLine3, invoiceLine4 } };
var invoiceHeader3 = new InvoiceHeader { Group = 'B', InvoiceLines = new List<InvoiceLine> { invoiceLine5, invoiceLine6 } };
var invoiceHeader4 = new InvoiceHeader { Group = 'B', InvoiceLines = new List<InvoiceLine> { invoiceLine7, invoiceLine8 } };
var invoiceHeaders = new List<InvoiceHeader>
{
invoiceHeader1,
invoiceHeader2,
invoiceHeader3,
invoiceHeader4
};
What I want to get is a Lists of invoiceLines for each Group.
So I would like for group A:
invoice1, invoice2, invoice3 and invoice4
and for group B:
invoice5, invoice6, invoice7 and invoice8
The furthest I got was:
var invoiceLinesGroupA = invoiceHeaders.SelectMany(x => x.InvoiceLines);
which as far as I can tell will get all eight invoiceLines. Somehow I need to discrimate by group to just get the ones for groupA and do likewise for groupB.
Can anyone help with this?
You may just want to group the invoice headers by the group:
var groups = invoiceHeader.GroupBy(ih => ih.Group);
Then you can access the lines of the groups:
foreach(var group in groups)
{
Console.WriteLine("Group " + group.Group);
Console.WriteLine("Lines:");
Console.WriteLine(string.Join(", ", group.SelectMany(h => h.InvoiceHeader.InvoiceLines)));
}
Output would be something like
Group A
Lines:
invoice1, invoice2, invoice3, invoice4
Group B
Lines:
invoice5, invoice6, invoice7, invoice8
Look at this overload of Enumerable.SelectMany
var result = invoiceHeaders
.SelectMany(x => x.InvoiceLines,
(group, InvoiceLine)=>{group, InvoiceLine})
.Where(res => res.group='A');
This should do it:
var test = from h in invoiceHeaders
from i in h.InvoiceLines
group i by h.Group
into g
select new {key = g.Key, rows = g.ToArray()};
You can then access the items like this test.Where(x => x.key == 'A').rows
var q = from current in myInvoiceHeaders
join c in myInvoiceLines on current.Id equals c.Header.Id into linesByHeader
select new {
Header = current,
Lines = linesByHeader
};
should work under the premise that you have a criteria to join both entities.
var regrouped = invoiceHeaders
.SelectMany(
header => header.InvoiceLines,
(header, line) => new { header, line }
)
.GroupBy(
o => o.header.Group,
o => o.line,
(groupName, lines) => new InvoiceHeader
{
Group = groupName,
InvoiceLines = lines.ToList()
}
)
.ToList();

How to merge two lists while adding metadata indicating value source with LINQ statement?

Given, for instance:
var a = new List<int>(){ 1 , 2 , 50 };
var b = new List<int>(){ 9 , 7 , 2 };
I need to merge them together to one sorted list, while adding some data indicating their origin (a or b). An output for example would be something like:
mergedList = { {1,false},{2,false},{2,true},{7,true},{9,true},{50,false} }
(true means it comes from a).
Edit start...
mergedList =
{ {1,IsB=false},{2,IsB=false},{2,IsB=true},{7,IsB=true},{9,IsB=true},{50,IsB=false} }
...Edit end
How can I do it with LINQ, preferably in query statement form (from ... select ...) ?
Not query form, but should work.
var ans = a.Select(i => new { Value = i, FromA = true })
.Concat(b.Select(i => new { Value = i, FromA = false }))
.OrderBy(i => i.Value);
You could create an anonymous type with the additional property:
var a = new List<int>(){ 1 , 2 , 50 };
var b = new List<int>(){ 9 , 7 , 2 };
var ax = a.Select(i => new{ Num = i, FromB = false });
var bx = b.Select(i => new{ Num = i, FromB = true});
var merged = ax.Concat(bx).OrderBy(x => x.Num);
Note that Enumerable.Concat will not eliminate dulicates, but since you want to add the origin i assume that this is desired.
Output:
foreach(var x in merged)
Console.WriteLine("Num: {0} From-B? {1}", x.Num, x.FromB);
Demo
var aItems = from aa in a
select new {Value = aa, Indicator = true};
var bItems = from bb in b
select new {Value = bb, Indicator = false};
var result = aItems.Concat(bItems).OrderBy(t => t.Value);
And pure method syntax:
var aItems = a.Select(aa => new {Value = aa, Indicator = true});
var bItems = b.Select(bb => new {Value = bb, Indicator = false});
var result = aItems.Concat(bItems).OrderBy(t => t.Value);
I think you can do this with anonymous types:
var mergedList = a.Select(x => new {val = x, tag = true})
.Union(b.Select(x => new {val = x, tag = false}))
.OrderBy(x => x.val);

Categories