I'm trying to group a result set of ektron search results items and output them on screen, I use the following code
var groupdResults =
from result in response.Results
orderby result[SearchSmartFormProperty.GetDateProperty("/root/FundClosingDate")]
group result by result[SearchSmartFormProperty.GetStringProperty("/root/DeadlineAltText")]
into statusGroup
select new
{
closingDate =statusGroup.Key,
count= statusGroup.Count
};
I then add these to a listview: uxSearchResultView.DataSource = groupdResults;
The problem I'm having is that i need to output all the data from the resultset e.g. title,closingdate, etc.., It currently only outputs, e.g.
Closing 2
open 1
really appreciate any help anyone can offer
-----------------------Updated-------------------------------------
I think i have a working solution now, but its kind of messy
var groupdResults = from result in response.Results
orderby result[SearchSmartFormProperty.GetDateProperty("/root/FundClosingDate")]
group result by result[SearchSmartFormProperty.GetStringProperty("/root/DeadlineAltText")]
into statusGroup
select new
{
closingDate = statusGroup.Key,
count = statusGroup.Count(),
items = statusGroup.ToList()
};
List<Ektron.Cms.Search.SearchResultData> SRDATA = new List<Ektron.Cms.Search.SearchResultData>();
foreach (var result in groupdResults)
{
for (int i = 0; i < result.items.Count; i++)
{
SRDATA.Add(result.items[i]);
}
}
Any input as to a cleaner implementation?
thanks
You can do following:
select new
{
closingDate =statusGroup.Key,
count= statusGroup.Count(),
items = statusGroup.ToList()
};
items property will contain items that were assigned to that group in a List.
(Modified)
I think you need a class to capture your results:
class GroupResult
{
public string Status { get; set; }
public ICollection<SearchResultData> Items { get; set; }
}
The grouping statusGroup is an IEnumerable of your items, so you can do:
var groupdResults =
from result in response.Results
group result by result[SearchSmartFormProperty.GetStringProperty("/root/DeadlineAltText")]
into statusGroup
select new GroupResult
{
Status =statusGroup.Key,
Items = statusGroup.ToList()
}.ToList();
After that you can display the Items in any way you wish, like sorted by Status and the Items sorted by ClosingDate.
But maybe it is enough to just sort response.Results by status and then by closing date.
Rather than using anonymous types I would put that into a new object.
var groupdResults =
from result in response.Results
orderby result[SearchSmartFormProperty.GetDateProperty("/root/FundClosingDate")]
group result by result[SearchSmartFormProperty.GetStringProperty("/root/DeadlineAltText")]
into statusGroup
select new ResultObject
{
closingDate = statusGroup.First().ClosingDate,
....
};
Related
I am trying to check if any string from e.ChatMessage.Message contains any TrigerWorld(WorldToTrigger) from model TrigerWord. And it should return correct phrase (FrazeCoSeObjevi) from TrigerWords. I tried it like this.
Thanks for any answer.
List<string> WordInMessage = e.ChatMessage.Message.Split(' ').ToList();
foreach (var item in WordInMessage)
{
var items = (from x in TrigerWorlds
join y in WordInMessage on x.Source equals y
select x).ToList():
}
There is the model class:
namespace FaxSoftware
{
public class TrigerWorlds
{
public string WorldToTriger { get; set; }
public string FrazeCoSeObjevi { get; set; }
}
}
Why not use .Intersect()?
var triggerWords = new List<string> {"hello", "world"};
var words = e.ChatMessage.Message.Split(' ').ToList();
var hasTrigger = words.Intersect(triggerWords).Any()
This will give you a boolean if there's any trigger word in message.
See documentation: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.intersect?view=netcore-3.1#code-try-2
According to me there are two ways for doing this.
Instead of using join use .Contains() method of string
List<string> WordInMessage = e.ChatMessage.Message.Split(' ').ToList();
var items = (from x in TrigerWorlds
where WordInMessage.Contains(x)
select x).ToList():
No need to convert e.ChatMessage.Message to list of string.
var items = (from x in TrigerWorlds
where e.ChatMessage.Message.Contains(x)
select x).ToList():
I am assuming you have a collection of TrigerWorlds object and you want a List of those that have matching word in World in Message
You dont need For loop. You can just have this
var items = (from x in triggerCol
join y in WordInMessage on x.WorldToTriger equals y
select x).ToList();
I have written a code like below:
foreach (var itemA in itm)
{
foreach (var itemB in filteredList)
{
if (itemA.ItemID != itemB.ItemID)
{
missingList.Add(itemB);
ListToUpdate.Add(itemB);
}
else
{
if (itemA.QuantitySold != itemB.QuantitySold)
{
ListToUpdate.Add(itemB);
}
}
}
}
So as you can see i have two lists here which are identical in their structure and they are:
List #1 is "itm" list - which contains old records from DB
List #2 is "filteredList" - which has all items from DB and + new ones
I'm trying to add items to missingList and ListToUpdate on next criteria:
All items that are "new" in filteredList - meaning their ItemID doens't exists in "itm" list should be added to missingList.
And all items that are new in filteredList- filteredList - meaning their ItemID doens't exists in "itm" list should be added to .ListToUpdate
And final criteria to add items to ListToUpdate should be those items that exist in both lists - and if the quantitysold in "itm" list is different - add them to ListToUpdate
The code above that I written gives me completely wrong results, I end up having more than 50000 items extra in both lists...
I'd like to change this code in a manner that it works like I wrote above and to possibly use parallel loops or PLINQ to speed things up...
Can someone help me out ?
Let's use Parallel.ForEach, which is available in C# 4.0:
Parallel.ForEach(filteredList, (f) =>
{
var conditionMatchCount = itm.AsParallel().Max(i =>
// One point if ID matches
((i.ItemID == f.ItemID) ? 1 : 0) +
// One point if ID and QuantitySold match
((i.ItemID == f.ItemID && i.QuantitySold == f.QuantitySold) ? 1 : 0)
);
// Item is missing
if (conditionMatchCount == 0)
{
listToUpdate.Add(f);
missingList.Add(f);
}
// Item quantity is different
else if (conditionMatchCount == 1)
{
listToUpdate.Add(f);
}
});
The above code uses two nested parallelised list iterators.
Following is an example to compare two lists which will give you list of new IDs.
Class I used to hold the data
public class ItemList
{
public int ID { get; set; }
}
Function to get new IDs
private static void GetNewIdList()
{
List<ItemList> lstItm = new List<ItemList>();
List<ItemList> lstFiltered = new List<ItemList>();
ItemList oItemList = new ItemList();
oItemList.ID = 1;
lstItm.Add(oItemList);
lstFiltered.Add(oItemList);
oItemList = new ItemList();
oItemList.ID = 2;
lstItm.Add(oItemList);
lstFiltered.Add(oItemList);
oItemList = new ItemList();
oItemList.ID = 3;
lstFiltered.Add(oItemList);
var lstListToUpdate = lstFiltered.Except(lstItm);
Console.WriteLine(lstListToUpdate);
}
For getting the list of common IDs use following
var CommonList = from p in lstItm
join q in lstFiltered
on p.ID equals q.ID
select p;
UPDATE 2
For getting the list of new IDs from filtered list based on ID
var lstListToUpdate2 = lstFiltered.Where(a => !lstItm.Select(b => b.ID).Contains(a.ID));
I have a List<List<string>> and need to filter the inner List. It looks like this:
{["/DownloadFile2.aspx?File=1026704PR20131001.PDF","10/1/2013 | Monthly Memo","10/1/2013","","","CI","","","","","",""],
["/DownloadFile2.aspx?File=1026704PR20141001.PDF","10/1/2014 | Monthly Memo","10/1/2014","","","CC","","","","","",""],
["/DownloadFile2.aspx?File=1026704date20130928.PDF","9/30/2013 | New Memo","9/30/2013","","","CC","","","","","",""],
["/DownloadFile2.aspx?File=1026704date20140928.PDF","9/30/2014 | New Memo","9/30/2014","","","CI","","","","","",""]}
How would I filter the second column using a LINQ .Where clause?
Like filtering on the term "Monthly" would return:
{["/DownloadFile2.aspx?File=1026704PR20131001.PDF","10/1/2013 | Monthly Memo","10/1/2013","","","CI","","","","","",""],
["/DownloadFile2.aspx?File=1026704PR20141001.PDF","10/1/2014 | Monthly Memo","10/1/2014","","","CC","","","","","",""]}
Adding Code
Archive Definition from my web service:
[DataContract(Namespace = "http://www.mysite.com")]
public class Archive
{
[DataMember]
public List<string> Header {get; set;}
[DataMember]
public List<List<string>> Rows{get; set;}
}
Code geting the Archive from the service (mps is the web service)
/// param contains values used to retrieve a partial set
/// as well as other values used to pass data to jQuery DataTable
///
Archive docs = mps.GetArchiveArray(LogonTicket, PID, param.iDisplayStart, param.iDisplayLength, "D");
List<List<string>> filteredRows;
// If the params sSearch value is not null or empty, filter the results
// else return the full results
if (!string.IsNullOrEmpty(param.sSearch))
{
// Here's where I need to filter the result set based on the params.sSeach value.
filteredRows = docs.Rows.?????;
}
else
{
filteredRows = docs.Rows;
}
The filteredRows are then passed through a loop to build my JSON using a StringBuilder and then the full desired result is sent as JSON:
string result = sb.ToString();
return Json(new
{
sEcho = param.sEcho,
iTotalRecords = numDocs,
iTotalDisplayRecords = numDocs,
aaData = result
},
JsonRequestBehavior.AllowGet);
Use the Select clause on the outer list, so something like:
outerList.Select(inr => inr.Where(...))
var lists = ...;
var filteredLists = (from list in lists
where list[1].EndsWith(" | Monthly Memo")
select list).ToList();
Or if it is only that it contains Monthly then
var lists = ...;
var filteredLists = (from list in lists
where list[1].Contains("Monthly")
select list).ToList();
Or lower/upper case doesn't matter then
var lists = ...;
var filteredLists = (from list in lists
where list[1].ToLower().Contains("monthly")
select list).ToList();
When introducing ToList the lambda syntax may be clearer:
var filteredLists = lists.Where(list => list[1].Contains("Monthly"))
.ToList()
EDIT If you want to check all columns:
var filteredLists = lists.Where(list => list.Any(s => s.Contains("Monthly")))
.ToList()
Use this (it works):
var listsWithMonthly = listOfLists.Where(x => x.Any(subx => subx.Contains("Monthly"))).ToList();
For filtering just on the second column in the inner list, this works too:
var listsWithMonthly = listOfLists.Where(x => x[1].Contains("Monthly")).ToList();
I'm new to Linq. I want to know whether this is the best way or are there any other ways to do this.
I have a requirement where from a web service, I receive a list of items:
class Item {
string ItemName { get; set;}
string GroupName { get; set; }
}
I receive the following data:
ItemName: Item1; GroupName: A
ItemName: Item2; GroupName: B
ItemName: Item3; GroupName: B
ItemName: Item4; GroupName: A
ItemName: Item5; GroupName: A
Now I want to get all of the unique Groups in the list, and associate all the Items to that Group. So I made a class:
class Group {
string GroupName { get; set; }
List<string> Items { get; set; }
}
So that there is a single group and all associated Items will be under the List.
I made two LINQ statements:
var uniqueGroups = (from g in webservice
where g.Group != null
select g.GroupName).Distinct();
Then I loop through it
foreach (var gn in uniqueGroups)
{
var itemsAssociated = (from item in webservice
where item.GroupName = gn.ToString()
select new {
});
}
and then I got the items, and save them to my object.
Is this the best way to do this or are there any LINQ statement that can do all these in one go?
Thanks.
Sounds like you want GroupBy
var itemsByGroup = items.GroupBy(i => i.GroupName);
foreach (var group in itemsByGroup)
{
var groupName = group.Key;
var itemsForThisGroup = group;
foreach (var item in itemsForThisGroup)
{
Console.Out.WriteLine(item.ItemName);
}
}
You can try this:
//List<Item> webservice = list with items from your webservice
var result = (from i in items
group i by i.GroupName into groups
select new Group()
{
GroupName = groups.Key,
Items = groups.Select(g => g.ItemName).ToList()
}).ToList();
I would use:
webservice.ToLookup(k => k.GroupName);
That would eliminate the need for the extra class.
Hope this helps!
That could be done all at once with an anonymous type and Enumerable.GroupBy:
var groupItems =
webservice.Where(i => i.GroupName != null)
.GroupBy(i => i.GroupName)
.Select(grp => new { Group = grp.Key, Items = grp.ToList() });
foreach (var groupItem in groupItems)
Console.WriteLine("Groupname: {0} Items: {1}"
, groupItem.Group
, string.Join(",", groupItem.Items.Select(i => i.ItemName)));
Distinct is useless since GroupBy will always make the groups distinct, that's the nature of a group.
Here's running code: http://ideone.com/R3jjZ
I've been using 101 LINQ Samples to get my feet wet using LINQ. It's been a good first resource, but I can't see an example there of what I currently need.
I just need to associate a sequential group number with each group. I have a working solution:
var groups =
from c in list
group c by c.Name into details
select new { Name = details.Key, DetailRecords = details };
int groupNumber = 0;
foreach (var group in groups)
{
//
// process each group and it's records ...
//
groupNumber++;
}
But, I'm sure it's possible to use LINQ to also generate the groupNumber. How?
This depends on your exact needs, but you can use:
var groupArray = groups.ToArray();
Similarly, you can use ToList. These data structures are sequential, and each group has an index.
If you do need the index on the object you create, another option is to use Select:
list.GroupBy(c => c.Name)
.Select((details, ind) =>
new
{
Name = details.Key,
DetailRecords = details,
Index = ind
});
this should do the trick:
int groupNumber = 0;
var groups =
from c in list
group c by c.Name into details
select new { Name = details.Key, DetailRecords = details, grpNum = groupNumber++};
if it's just a sequential group number, just use the Count() method on your IEnumerable.
var groups =
from c in list
group c by c.Name into details
select new {Name = details.Key, DetailRecords = details};
for(int i = 0; i < groups.Count(); i++)
{
//Process Records
}
Then, if you need the specific group number, you can just grab i.