Add query to linq var - c#

I never used this before. I need to add another member to the query. How can I add "link" to "source"?
var titles = regexTitles.Matches(pageContent).Cast<Match>();
var dates = regexDate.Matches(pageContent).Cast<Match>();
var link = regexLink.Matches(pageContent).Cast<Match>();
var source = titles.Zip(dates, (t, d) => new { Title = t, Date = d });
foreach (var item in source)
{
var articleTitle = item.Title.Groups[1].Value;
var articleDate = item.Date.Groups[1].Value;
//var articleLink = item.Link.Groups[1].Value;
Console.WriteLine(articleTitle);
Console.WriteLine(articleDate);
//Console.WriteLine(articleLink);
Console.WriteLine("");
}
Console.ReadLine();

It sounds like you just need another call to Zip. The first call will pair up the titles and dates, and the second call will pair up the title/date pairs with the links:
var source = titles.Zip(dates, (t, d) => new { t, d })
.Zip(link, (td, l) => new { Title = td.t,
Date = td.d,
Link = l });
or (equivalently, just using projection initializers):
var source = titles.Zip(dates, (Title, Date) => new { Title, Date })
.Zip(link, (td, Link) => new { td.Title, td.Date, Link });
(I sometimes think it would be nice to have another couple of overloads for Zip to take three or four sequences... it wouldn't be too hard. Maybe I'll add those to MoreLINQ :)

You can easily use Zip once more as in the below code:
var source = titles.Zip(dates, (t, d) => new { Title = t, Date = d });
.Zip(link, (d, l) => new { Title = d.Title,
Date = d.Date,
Link= l });

Related

display two table data at a time in linq

I want to to display two tables information at a time.
List<int> order_track = db.Order_Trackings.Where(e => e.UID == id).Select(q => q.ID).ToList();
if (order_track == null)
{
var rate = db.Ratings.OrderByDescending(e => e.Rate).Take(5);
}
List<int> fidList = db.OrderFoods.Where(q => order_track.Contains(q.OID)).Select(q => q.FID).ToList();
var qs = (from x in fidList
group x by x into g
let count = g.Count()
orderby count descending
select new { KEY = g.Key });
if (order_track.Count == 2)
{
var one = qs;
List<int> idList = new List<int>();
foreach (var val in one)
{
idList.Add(val.KEY);
}
var food = db.Foods.Where(q => idList.Contains(q.ID));
var rate = db.Ratings.OrderByDescending(e => e.Rate).FirstorDefault();
return Request.CreateResponse(HttpStatusCode.OK, rate);
I want to do something like this I hope you will understand what i am trying to achieve Thanks in advance.
var food = db.Foods.Where(q => idList.Contains(q.ID)&&db.Ratings.OrderByDescending(e => e.Rate).FirstorDefault());
return Request.CreateResponse(HttpStatusCode.OK, rate);
If you want to combine the two results into one variable, then the easiest way to do so is by creating an anonymous object, like this:
var result = new
{
food = db.Foods.Where(q => idList.Contains(q.ID)),
rate = db.Ratings.OrderByDescending(e => e.Rate).FirstorDefault()
};
return Request.CreateResponse(HttpStatusCode.OK, result);
You could also create a class with two properties and then create an instance of that class, but if this is the only place where you would use that class then I wouldn't bother doing that.

Unable to get a distinct list that contains summed values

I have posted this earlier but the objective of what I am trying to achieve seems to have lost hence re-posting it to get explain myself better.
I have a collection that has duplicate productnames with different values. My aim is to get a list that would sum these productnames so that the list contains single record of these duplicates.
For e.g
If the list contains
Product A 100
Product A 200
The result object should contain
Product A 300
So as you can see in my code below, I am passing IEnumerable allocationsGrouped to the method. I am grouping by productname and summing the Emv fields and then looping it so that I created a new list of the type List and pass it to the caller method. The problem what I seeing here is on the following line of code Items = group. Items now contains original list without the sum. Hence the inner foreach loop runs more than ones because there are duplicates which defeats my purpose. I finally need to return result object that has non duplicate values which are summed based on the above criteria. Could you please tell me where I am going wrong.
private static List<FirmWideAllocationsViewModel> CreateHierarchy(string manStratName, IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> allocationsGrouped, List<FirmWideAllocationsViewModel> result)
{
var a = allocationsGrouped
.Where(product => !string.IsNullOrEmpty(product.PRODUCT_NAME))
.GroupBy(product => product.PRODUCT_NAME)
.Select(group => new
{
ProductName = group.Key, // this is the value you grouped on - the ProductName
EmvSum = group.Sum(x => x.EMV),
Items = group
});
var b = a;
var item = new FirmWideAllocationsViewModel();
item.Hierarchy = new List<string>();
item.Hierarchy.Add(manStratName);
result.Add(item);
foreach (var ac in b)
{
var productName = ac.ProductName;
var emvSum = ac.EmvSum;
foreach (var elem in ac.Items)
{
var item2 = new FirmWideAllocationsViewModel();
item2.Hierarchy = new List<string>();
item2.Hierarchy.Add(manStratName);
item2.Hierarchy.Add(elem.PRODUCT_NAME);
item2.FirmID = elem.FIRM_ID;
item2.FirmName = elem.FIRM_NAME;
item2.ManagerStrategyID = elem.MANAGER_STRATEGY_ID;
item2.ManagerStrategyName = elem.MANAGER_STRATEGY_NAME;
item2.ManagerAccountClassID = elem.MANAGER_ACCOUNTING_CLASS_ID;
item2.ManagerAccountingClassName = elem.MANAGER_ACCOUNTING_CLASS_NAME;
item2.ManagerFundID = elem.MANAGER_FUND_ID;
item2.ManagerFundName = elem.MANAGER_FUND_NAME;
item2.Nav = elem.NAV;
item2.EvalDate = elem.EVAL_DATE.HasValue ? elem.EVAL_DATE.Value.ToString("MMM dd, yyyy") : string.Empty;
item2.ProductID = elem.PRODUCT_ID;
item2.ProductName = elem.PRODUCT_NAME;
item2.UsdEmv = Math.Round((decimal)elem.UsdEmv);
item2.GroupPercent = elem.GroupPercent;
item2.WeightWithEq = elem.WEIGHT_WITH_EQ;
result.Add(item2);
}
}
return result;
}
change it to:
var result = allocationsGrouped
.Where(product => !string.IsNullOrEmpty(product.PRODUCT_NAME))
.GroupBy(product => product.PRODUCT_NAME)
.Select(group => {
var product = group.First();
return new FirmWideAllocationsViewModel()
{
Hierarchy = new List<string>() { manStratName, product.PRODUCT_NAME },
FirmID = product.FIRM_ID,
FirmName = product.Item.FIRM_NAME,
ManagerStrategyID = product.MANAGER_STRATEGY_ID,
ManagerStrategyName = product.MANAGER_STRATEGY_NAME,
ManagerAccountClassID = product.MANAGER_ACCOUNTING_CLASS_ID,
ManagerAccountingClassName = product.MANAGER_ACCOUNTING_CLASS_NAME,
ManagerFundID = product.MANAGER_FUND_ID,
ManagerFundName = product.MANAGER_FUND_NAME,
Nav = product.NAV,
EvalDate = product.EVAL_DATE.HasValue ? product.EVAL_DATE.Value.ToString("MMM dd, yyyy") : string.Empty,
ProductID = product.PRODUCT_ID,
ProductName = product.PRODUCT_NAME,
UsdEmv = Math.Round((decimal)product.UsdEmv),
GroupPercent = product.GroupPercent,
WeightWithEq = product.WEIGHT_WITH_EQ,
//assign aggregate Sum here
EmvSum = group.Sum(x => x.EMV),
};
});

LINQ GroupBy into a new object

We are working on some LINQ stuff and are new to using the GroupBy extension.
I am editing this post to include my actual code as I tried to use some simple example but it seems that it making it more confusing for those trying to help. Sorry for that.
NOTE We need to sum the Amount field below. We did not attempt that yet as we are just trying to figure out how to extract the list from the groupBy.
Here is my code:
myCSTotal2.AddRange(userTotals.Where(w => w.priceinfoId == priceinfoID).GroupBy(g => g.termLength, o => new Model.MyCSTotal2
{
PriceinfoID = o.priceinfoId,
BillcodeID = o.billcodeid,
JobTypeID = o.jobtypeID,
SaleTypeID = o.saletypeID,
RegratesID = o.regratesID,
NatAccPerc = o.natAcctPerc,
NatIgnInCommCalc = o.natIgnInCommCalc,
TermLength = (int)o.termLength,
Amount = o.RMR1YrTotal / 12,
RuleEvaluation = 0
}).Select(grp => grp.ToList()));
The error we get when trying to do this is:
Argument 1: cannot convert from
IEnumerable<List<MyCSTotal2>> to IEnumerable<MyCSTotal2>
EDIT: Thanks for the help. Here is what we ended up with:
myCSTotal2.AddRange(userTotals.Where(w => w.priceinfoId == priceinfoID)
.GroupBy(g => g.termLength)
.SelectMany(cl => cl.Select( o => new Model.MyCSTotal2
{
PriceinfoID = o.priceinfoId,
BillcodeID = o.billcodeid,
JobTypeID = o.jobtypeID,
SaleTypeID = o.saletypeID,
RegratesID = o.regratesID,
NatAccPerc = o.natAcctPerc,
NatIgnInCommCalc = o.natIgnInCommCalc,
TermLength = (int)o.termLength,
Amount = cl.Sum(m=>m.RMR1YrTotal / 12),
RuleEvaluation = 0
})));
In order to flatten the groups you need to use SelectMany extension method:
SelectMany(grp => grp.ToList())
But if that is your current query you don't need to group, you need to project your collection using Select:
myCSTotal2.AddRange(userTotals.Where(w => w.priceinfoId == priceinfoID)
.Select( o => new Model.MyCSTotal2
{
PriceinfoID = o.priceinfoId,
BillcodeID = o.billcodeid,
JobTypeID = o.jobtypeID,
SaleTypeID = o.saletypeID,
RegratesID = o.regratesID,
NatAccPerc = o.natAcctPerc,
NatIgnInCommCalc = o.natIgnInCommCalc,
TermLength = (int)o.termLength,
Amount = o.RMR1YrTotal / 12,
RuleEvaluation = 0
});
I see no reason in using GroupBy as there are no aggregation functions involved. If you want to have Persons distinct by termLength. Write a DistinctBy. You will get the desired collection this way
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
HashSet<TKey> seenKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (seenKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
Then use the extension like this
var collection = userTotals
.Where(w => w.priceinfoId == priceinfoID)
.DistinctBy(g => g.termLength)
.Select(o => new Model.MyCSTotal2
{
PriceinfoID = o.priceinfoId,
BillcodeID = o.billcodeid,
JobTypeID = o.jobtypeID,
SaleTypeID = o.saletypeID,
RegratesID = o.regratesID,
NatAccPerc = o.natAcctPerc,
NatIgnInCommCalc = o.natIgnInCommCalc,
TermLength = (int)o.termLength,
Amount = o.RMR1YrTotal / 12,
RuleEvaluation = 0
});

Filter and add values using C# using lambda expression

New to C# and appreciate any help. The issue is that I need to filter the results of my api call against an array (using an "allowedA" and "allowedB" array.) I don't know how to edit the lambda expression to check against the loop.
var activities = await _restClientTaxonomy.GetTaxonomyFullAsync(TAXONOMY_CLASSIFICATIONID_FOR_ACTIVITY);
var activityTypes = await _restClientTaxonomy.GetTaxonomyFullAsync(TAXONOMY_CLASSIFICATIONID_FOR_ACTIVITY_TYPES);
var documentEventxx = activities.Select(type => type.Id);
long [] allowedA = new long []{ 7137, 40385637};
long [] allowedB = new long []{ 7137, 40385637};
foreach (long value in documentEventxx)
{
foreach (var item in allowed)
{
if (item == value) {
//These are the values I am looking for -> values that are part of the documentEventxx and allowedB.
}
}
}
var result = activityTypes.Select(type => new CategoryViewModel
{
Id = type.Id,//This is where I want to add only items that are in the allowedA array
Text = type.Name,
Types = activities.Where(a => a.ParentId == type.Id).Select(t => new TaxonomyMemberTextItem
{
Id = t.Id, //This is where I want to add only items that are in the allowedB array
Text = t.Name
}).ToList()
}).ToArray();
I have been reading about lambda expressions and foreach loops so please don't just post a random link.
Thanks in advance.
Filter the values before Selecting.
activityTypes.Where(x=>allowedA.Contains(x.Id)).Select(type => new CategoryViewModel
{
Id = type.Id,
Text = type.Name,
Types = activities.Where(a => a.ParentId == type.Id && allowedB.Contains(a.Id)).Select(t => new TaxonomyMemberTextItem
{
Id = t.Id,
Text = t.Name
}).ToList()
})
To filter you use .Where. You .Select to create a list of new types. So in order to filter, then create the lists of objects you want:
var result = activityTypes.Where(type=>isAllowed(type.Id)).Select(type => new CategoryViewModel
{
Id = type.Id,//This is where I want to add only items that are in the allowedA array
Text = type.Name,
Types = activities.Where(a => a.ParentId == type.Id&&isAllowed(a.Id)).Select(t => new TaxonomyMemberTextItem
{
Id = t.Id, //This is where I want to add only items that are in the allowedB array
Text = t.Name
}).ToList()
}).ToArray();

Distinct concat two IEnumerables and flag if it came from list B

Wood for the trees moment I'm sure.
var dateListA = new List<DateTime>();
var dateListB = new List<DateTime>();
for (var date = DateTime.Parse("01-Dec-2013"); date <= DateTime.Parse("08-Dec-2013"); date = date.AddDays(1))
dateListA.Add(date);
for (var date = DateTime.Parse("05-Dec-2013"); date <= DateTime.Parse("10-Dec-2013"); date = date.AddDays(1))
dateListB.Add(date);
var results = distinct_Merge_Of_dateListA_And_dateListB_But_With_New_WasInListB_Boolean_Property => new { Date, WasInListB };
foreach (var result in results)
{
System.Console.WriteLine("{0} {1}", result.Date, result.WasInListB);
}
Expected output
01-Dec-2013 No
02-Dec-2013 No
03-Dec-2013 No
04-Dec-2013 No
05-Dec-2013 Yes
06-Dec-2013 Yes
07-Dec-2013 Yes
08-Dec-2013 Yes
09-Dec-2013 Yes
10-Dec-2013 Yes
I'm trying two merge to lists but indicate in the final list (with a boolean) if the result was included in list B. Any hints on how to do is are appreciated.
You can use:
var newInA = dateListA.Except(dateListB);
var results = newInA
.Select(d => new { Date = d, WasInListB = false })
.Concat(dateListB.Select(d => new { Date = d, WasInListB = true }));
This will give you the results you listed. It lists items that are only found in A first, then all items from B.
Use a dictionary. Classic bin approach:
var results = new Dictionary<DateTime,bool>();
foreach( var date in dateListA )
results[date] = false;
foreach( var date in dateListB )
results[date] = true;
foreach (var result in results)
System.Console.WriteLine("{0} {1}", result.Key, result.Value);
Hope this helps,
Just tested and I believe this does just what you are looking for...although a more efficient method may be possible.
var dateData = dateListB.Select(x => new { Date = x, InB = true }).Union(dateListA.Except(dateListB).Select(x => new { Date = x, InB = false })).ToList();
Good Luck!
If you want to discard all dates from B that are also in A, you could achive this with a GroupBy, then selecting only the first element from each group (which will be from A if there are two, since A was added first
dateListA.Select(a => new { Date = a, FromB = false })
.Concat(dateListB.Select(b => new { Date = b, FromB = true })
.GroupBy(x=>x.Date)
.Select(gp=> gp.First());
If you want to know whether an item occured in A and in B, you could use
dateListA.Select(a => new { Date = a, FromB = false })
.Concat(dateListB.Select(b => new { Date = b, FromB = true })
.GroupBy(x=>x.Date)
.Select(gp=> new {Date = gp.Key, FromA = gp.Any(x=>!x.FromB), FromB = gp.Any(x=>x.FromB));

Categories