Combine lists that have at least one item in common - groovy

I have a list of lists in groovy. Some of the nested lists have certain items in common. I would like for all of the nested lists that have at least one item in common to join into one list, for example:
Extract of my list of lists :
[[buy, order, bought, purchase],
[opinion, point of view],
[opinion, belief],
[buy, purchased],
[buy, order, purchases]]
(The order of nested lists is random)
What I would like to achieve :
[[buy, order, bought, purchase, buy, purchased, buy, order, purchases],
[opinion, point of view, opinion, belief]]
Anybody has any suggestions on how to achieve this?
Thank you!

You can do the following with inject:
def input = [['buy', 'order', 'bought', 'purchase'],
['opinion', 'point of view'],
['opinion', 'belief'],
['buy', 'purchased'],
['buy', 'order', 'purchases']]
input.inject([]) { list, current ->
list.find { it.intersect(current) }?.addAll(current) ?: list << current
list
}
So, find an element in the output list that intersects with the current input list, and if it exists add it to that output list.
If one isn't found, add the input list to the output list

Related

How do I filter a list of LiveData objects in Kotlin?

The title might be worded weirdly or unclear, but I am creating a game using android studio and Kotlin as the language. I have a repository that retrieves the score to the game (also stores it):
val readAllData: LiveData<List<ScoreDB>> = scoreDao.getScore()
Then in my leaderboard composable function I have:
val scoreList : LiveData<List<ScoreDB>> = vm.readAllData
I want to filter out this list to display the top 10 scores. After scoreList is filtered to only the top ten score, I was going to put it in a lazyColumn using something like this:
//TODO List highest scores from database in this lazycolumn
items(10){idx->
ScoreRow(idx)
}
I am stuck on how to filter the scoreList to contain only the top 10 scores and then to display them in the lazy column. Thanks for the help
You can use sortedByDescending() to filter the list, and use take() to get the first 10 elements. If you want to show it, you should create a new LivaData to store your filterd list:
val topTenScoreList : LiveData<List<ScoreDB>> =
Transformations.map(scoreList) {
it.sortedDescending{ s->s.score }.take(10)
}
And use topTenScoreList to generate columns
I want to filter out this list to display the top 10 scores.
OK, so you need
a list
to the sort the list highest to lowest and
to take the first 10 of that:
So:
val topTenScores = scoreList // The live data
.value // The actual list
.sortedByDescending { it.score } // The list sorted by ScoreDB.score
.take(10) // And filtering out the first 10

Building a std::map and issue with using std::emplace

Code:
std::map<CString, S_DISCUSSION_HIST_ITEM> mapHistory;
// History list is in ascending date order
for (auto& sHistItem : m_listDiscussionItemHist)
{
if (m_bFullHistoryMode)
mapHistory.emplace(sHistItem.strName, sHistItem);
else if (sHistItem.eSchool == m_eActiveSchool)
mapHistory.emplace(sHistItem.strName, sHistItem);
}
// The map is sorted by Name (so reset by date later)
// The map has the latest assignment info for each Name now
Observation:
I now understand that std::emplace behaves like this:
The insertion only takes place if no other element in the container has a key equivalent to the one being emplaced (keys in a map container are unique).
Therefore my code is flawed. What I was hoping to acheive (in pseudo code) is:
For Each History Item
Is the name in the map?
No, so add to map with sHitItem
Yes, so replace the sHistItem with this one
End Loop
By the end of this loop iteration I want to have the most recent sHitItem, for each person. But as it is, it is only adding an entry into the map if the name does not exist.
What is the simplest way to get around this?
Use insert_or_assign method if the item is assignable. It will be assigned if it already exists. Or use [] operator followed by assignment, it will default-construct item if it does not exist.
For non-assignable types I'm afraid there's no convenient way.

dict comprehension with list as value

I have a program that prompts the user for three pieces of input:
Student No.
Name
Age
I then populate a list with this info. This currently works fine.
I was wanting to change the list construct to a dictionary that has the Student No. as the key and a list with the Name and Age as values. I also want the list in case I decide to enlarge the number of inputs in the future.
I was wondering if I could use a dict comprehension but I can't find any examples of using a dict comp being used to create a dict with a list as the value.
The resulting dictionary would look like:
st_dict = {101: [Mark, 54],
102: [Bob, 49]
}
Is it possible, using a dict comp, to create this structure?
Basically I am wanting to do this:
st_dict = {Student_no: [Name, Age] per student entered}

Efficient way to determine if there is more than one distinct item using linq.js

I'm looking for an efficient way using linq.js to determine if a collection has more than one distinct value. I assume that the following approach is inefficient because it has to consider the entire collection.
if (Enumerable.From(collection).Distinct().Take(2).Count() > 1) {
//it's not unique, continue loop
}
My question is similar to one:
Efficient Linq Enumerable's 'Count() == 1' test
Is there a more efficient linq.js-based technique? Thanks!
If you're specifically testing to see if a collection has more than one item in it, the idiomatic way to write it (IMHO) is to use Skip in conjunction with Any. Skip the first item and if there are any others in the collection, it has more than one. If it was empty, the Skip would effectively do nothing and there still wouldn't be any other items in the collection.
In your case, your condition would be:
if (Enumerable.From(collection).Distinct().Skip(1).Any()) {
//it's not unique, continue loop
}
var test = collection[0];
if (Enumerable
.From(collection)
.Skip(1)
.Any(function (e) { return e != test; })
)
Let me explain it. At least 2 distinct items mean that for any item there is at least one item that is not equal to it. Let's pick first item, you could pick any other, just first is more convenient and let's see if there is any other number not equal to it (except itself).

Couchdb: filter and group in a single view

I have a Couchdb database with documents of the form: { Name, Timestamp, Value }
I have a view that shows a summary grouped by name with the sum of the values. This is straight forward reduce function.
Now I want to filter the view to only take into account documents where the timestamp occured in a given range.
AFAIK this means I have to include the timestamp in the emitted key of the map function, eg. emit([doc.Timestamp, doc.Name], doc)
But as soon as I do that the reduce function no longer sees the rows grouped together to calculate the sum. If I put the name first I can group at level 1 only, but how to I filter at level 2?
Is there a way to do this?
I don't think this is possible with only one HTTP fetch and/or without additional logic in your own code.
If you emit([time, name]) you would be able to query startkey=[timeA]&endkey=[timeB]&group_level=2 to get items between timeA and timeB grouped where their timestamp and name were identical. You could then post-process this to add up whenever the names matched, but the initial result set might be larger than you want to handle.
An alternative would be to emit([name,time]). Then you could first query with group_level=1 to get a list of names [if your application doesn't already know what they'll be]. Then for each one of those you would query startkey=[nameN]&endkey=[nameN,{}]&group_level=2 to get the summary for each name.
(Note that in my query examples I've left the JSON start/end keys unencoded, so as to make them more human readable, but you'll need to apply your language's equivalent of JavaScript's encodeURIComponent on them in actual use.)
You can not make a view onto a view. You need to write another map-reduce view that has the filtering and makes the grouping in the end. Something like:
map:
function(doc) {
if (doc.timestamp > start and doc.timestamp < end ) {
emit(doc.name, doc.value);
}
}
reduce:
function(key, values, rereduce) {
return sum(values);
}
I suppose you can not store this view, and have to put it as an ad-hoc query in your application.

Resources