How to keep a groupedby list sorted in Play Framework 2 templates - scala

I've got a list of complex objects that I want to display, grouped by one of its attribute in a Play 2 template.
I managed to do it :
#measures.groupBy(_.question.category).map {
case (category, items) => {
// Category stuff
#for(item <- items) {
// List of items
}
}
}
The problem is that the list was sorted in my Java controller, but the keyset of the map that I create is not sorted anymore (I would like to sort the key set using something like _.question.category.order).
Is there a way to have a sorted map on this attribute?
Thanks !

What type is measures? Did you try to use a LinkedHashSet? This should keep the elements order in contrast to e.g. a HashSet.

Related

Search for child items matching certain name values

This page describes how to retrieve an item, (immediate) child items and searching in Sitecore9 using the RESTful API (via PostMan).
What it doesn't appear to say is how to combine those queries.
I would like to search the children of an item which is specified by path. So, currently, I have this returning an item:
GET https://{{sitecorehost}}/sitecore/api/ssc/aggregate/content/Items('{{sitecorehome}}/banners-tests')?sc_apikey={{sitecore-master-apikey}}
I also have this returning the child items of that item:
GET https://{{sitecorehost}}/sitecore/api/ssc/aggregate/content/Items('{{sitecorehome}}/banners-tests/Subcontent/Image and Texts')/Children?sc_apikey={{sitecore-master-apikey}}
However, because the children are not immediate children - they are two levels down at /Subcontent/Image and Texts - I cannot request them. Yes, I could search for them, but then any items would come back with the matching criteria and I only want to search items under that particular path.
I would like something which, I imagine, would look something like this:
GET https://{{sitecorehost}}/sitecore/api/ssc/aggregate/content/Items?sc_apikey={{sitecore-master-apikey}}&$filter=Name eq 'banner' and Path eq 'banners-tests'
Or perhaps this:
GET https://{{sitecorehost}}/sitecore/api/ssc/aggregate/content/Items('{{sitecorehome}}/banners-tests')/Children?sc_apikey={{sitecore-master-apikey}}&$filter=Name eq 'banner'
But these do not work.
#Matt We can do filtering based on the item path. For example, considering item path as :
'sitecore/content/home/tenant1/Subcontent/Image and Texts/neededitem' - the one needed
'sitecore/content/home/tenant1/Subcontent/Image and
Texts/item1/neededitem/notneededitem' - the one we need to exclude
since '/' is not a valid character in the Sitecore item name and indicates the children of the needed item.
Hence, it can be used as a filter in javascript.
So we can split by 'Image and Texts' and then find the items.
For example, consider an array of results and let us say object with a collection of items is items and item path of each item is denoted by Path(let's say, this can be some other property as well) property
let items = [{
Path: 'sitecore/content/home/tenant1/Subcontent/Image and Texts/neededitem',
anotherProperty: 'text-val1'
}, {
Path: 'sitecore/content/home/tenant1/Subcontent/Image and Texts/item1/neededitem/notneededitem',
anotherProperty: 'text-val2'
}];
const results = items.filter(item => {
const splittedPath = item.Path.split('Image and Texts');
if (splittedPath[1].split("/").length <= 2) {
return item;
}
});
console.log(results);
In case your SSC controller (C#) is custom one and having access to Sitecore Context object or Sitecore APIs then the GetChildren() method of Item class will bring children of first level only.
I hope this helps.

Possible to do two Group Bys and an if count check in one LINQ lambda statement?

Kind of a specific question but I wasn't sure how to approach it. I've got a list of rooms, that I am trying to group first by type, then by owner. I am doing this to check if there are duplicate rooms for a given owner and type (which shouldn't be possible so I need to prune them out). Right now my code looks like this:
IQueryable<IGrouping<Guid, Room>> allRoomsByOwner = _dbContext.Rooms.GroupBy(x => x.OwnerId);
List<Room> duplicates = new List<Room>();
foreach (IGrouping<Guid, Room> roomsByOwner in allRoomsByOwner)
{
IEnumerable<IGrouping<Guid, Room>> roomsOfOwnerByType = roomsByOwner.ToList().GroupBy(x => x.TypeId);
foreach (IGrouping<Guid, Room> grouping in roomsOfTypeByType)
{
if (grouping.Count() > 1)
{
duplicates.AddRange(grouping.ToList());
}
}
}
I'm just wondering if it's possible to put this all into one LINQ statement? I've got similar things before, but not quite this complex and not using two group bys. Thanks.
You can group by multiple columns ( OwnerId and TypeId) and flatten the groups with more than one elements (using the SelectMany method) to get the duplicates:
var duplicates = _dbContext.Rooms.GroupBy(x => new{x.OwnerId,x.TypeId})
.Where(g=>g.Count()>1)
.SelectMany(g=>g.Skip(1))// If you like you can skip the first element as representative of the group and the treat the rest as a duplicate.
.ToList();

Composite views in couchbase

I'm new to Couchbase and am struggling to get a composite index to do what I want it to. The use-case is this:
I have a set of "Enumerations" being stored as documents
Each has a "last_updated" field which -- as you may have guessed -- stores the last time that the field was updated
I want to be able to show only those enumerations which have been updated since some given date but still sort the list by the name of the enumeration
I've created a Couchbase View like this:
function (doc, meta) {
var time_array;
if (doc.doc_type === "enum") {
if (doc.last_updated) {
time_array = doc.last_updated.split(/[- :]/);
} else {
time_array = [0,0,0,0,0,0];
}
for(var i=0; i<time_array.length; i++) { time_array[i] = parseInt(time_array[i], 10); }
time_array.unshift(meta.id);
emit(time_array, null);
}
}
I have one record that doesn't have the last_updated field set and therefore has it's time fields are all set to zero. I thought as a first test I could filter out that result and I put in the following:
startkey = ["a",2012,0,0,0,0,0]
endkey = ["Z",2014,0,0,0,0,0]
While the list is sorted by the 'id' it isn't filtering anything! Can anyone tell me what I'm doing wrong? Is there a better composite view to achieve these results?
In couchbase when you query view by startkey - endkey you're unable to filter results by 2 or more properties. Couchbase has only one index, so it will filter your results only by first param. So your query will be identical to query with:
startkey = ["a"]
endkey = ["Z"]
Here is a link to complete answer by Filipe Manana why it can't be filtered by those dates.
Here is a quote from it:
For composite keys (arrays), elements are compared from left to right and comparison finishes as soon as a element is different from the corresponding element in the other key (same as what happens when comparing strings à la memcmp() or strcmp()).
So if you want to have a view that filters by date, date array should go first in composite key.

Iterate Set in order Play Framework

I pass my template a TreeSet with Strings. However, when I loop over the set like this:
#(usernames : TreeSet[String])
#for( name <- usernames){
#name ,
}
However, the names are never printed in the correct order.
How can I iterate over my set in my template and print the names in order?
This has something to do with the way Scala Templates work. I suspect your TreeSet collection is under the hood mapped to a different collection and as a result the ordering is not preserved.
There is clearly a difference between the behavior of the Scala for loop and the for loop in Scala Templates. If you run your code as regular Scala code the order of the TreeSet is obviously preserved:
val users = TreeSet("foo", "bar", "zzz", "abc")
for (user <- users) {
println(user)
}
One of the ways to solve the problem is to use the iterator in the Scala Template:
#for(name <- usernames.iterator) {
#name ,
}
or transform the TreeSet to a sequence:
#for(name <- usernames.toSeq) {
#name ,
}
There is no guaranteed ordering for any Set class, so it's best to sort it before iterating.
If you mean to print them alphabetically, you should convert it into a List and then iterate
#(usernames : TreeSet[String])
#for( name <- usernames.toList().sortWith(_ < _)){
#name ,
}

Entity Framework IN clause on navigation property

I have a object IQueryable which I am trying to dynamically add Where clauses, this works great for columns on Listing object, however now conditionally I would like to add a IN clause to a navigation property (ListingAmenities) which has columns ListingID, AmenityID
I have critiera.AmenityIDs which may contain Amenities I like to filter the results on.
I am trying to achieve the following if there is any amenityIDs in my array
select * from Listings l inner join ListingAmenities a on l.ListingID = a.ListingID where a.AmenityID IN(1,2,3)
here is my code (I am using EF5)
if (criteria.AmenityIDs.Count > 0)
{
listings = listings.Where(x => x.ListingAmenities.Any(y => y.AmenityID == criteria.AmenityIDs));
}
which of course does not work.
Important note is that I am adding these WHERE clauses dynamically so that is why I am building up a IQueryable object
Enumerable.Contains translates into IN in SQL, so you can use:
if (criteria.AmenityIDs.Count > 0)
{
listings = listings.Where(x => x.ListingAmenities
.Any(y => criteria.AmenityIDs.Contains(y.AmenityID)));
}
Be careful if the AmenityIDs collection is very long because Contains has performance problems for large collections. But 100 elements or so shouldn't be a problem.