Sorting Map values in descending order with Groovy - sorting

I have a Map<String,Integer> whose entries (keys) need to be sorted in order of descending value. For instance, if the map looks like:
"a" => 5
"b" => 3
"c" => 12
"d" => 9
The after sorting it needs to look like:
"c" => 12
"d" => 9
"a" => 5
"b" => 3
My best attempt thus far:
def test() {
Map<String,Integer> toSort = new HashMap<String,Integer>()
toSort.put("a", 5)
toSort.put("b", 3)
toSort.put("c", 12)
toSort.put("d", 9)
Map<String,Integer> sorted = sortMapDesc(toSort)
sorted.each {
println "${it.key} has a value of ${it.value}."
}
}
def sortMapDesc(Map<String,Integer> toSort) {
println "Sorting..."
println toSort
// The map of properly sorted entries.
Map<String,Integer> sorted = new HashMap<String,Integer>()
// Keep scanning the map for the key with the highest value. When we find
// it, add it as the next entry to the 'sorted' map, and then zero it out
// so it won't show up as the highest on subsequent scans/passes. Stop scanning
// when the entire 'toSort' map contains keys with zeros.
while(!mapIsAllZeros(toSort)) {
int highest = -1
String highestKey = ""
toSort.each {
if(it.value > highest) {
highest = it.value
highestKey = it.key
}
}
toSort.put(highestKey, 0)
sorted.put(highestKey, highest)
}
sorted
}
def mapIsAllZeros(Map<String,Integer> toCheck) {
toCheck.values().every{!it}
}
When I run test() I get the following output:
Sorting...
[d:9, b:3, c:12, a:5]
d has a value of 9.
b has a value of 3.
c has a value of 12.
a has a value of 5.
Where am I going wrong here?

Just do:
​def m = [a:​5, b:12, c:3, d:9]
def sorted = m.sort { a, b -> b.value <=> a.value }

To do the sorting, Tim's implementation is the way to go. But if you're just wondering why your example code doesn't work as you expect, the answer is that the variable 'sorted' needs to be of type LinkedHashMap, rather than just HashMap. You can set it explicitly:
Map<String,Integer> sorted = new LinkedHashMap<String,Integer>()
Or, just do this:
Map<String,Integer> sorted = [:]

Related

add data to an arrayList, when If condition is satisfied in nested for loop in Kotlin

Check the code below:
getCommonsArrayList(listA:ArrayList< User >, listB:ArrayList<User>):ArrayList<User>{
var listCommon = ArrayList<User>()
for (i in listA.indices) {
for (j in listB.indices) {
if (listA[i].id.equals(listB[j].id)) { //if id of the user matches
listCommon.put(listA[i]) //add to a new list
}
}
}
return listCommon // return the new list with common entries
}
The above method iterates list a & b and check whether the id's are matching, if they are then the User object is stored to a new list and at the end of the program, it returns the common list.
This thing works good. And I hope nested for followed by if condition is the way in which we can compare two lists.
The problem with this is if listA has repeated entries, then the listCommon will also have repeated entries as ArrayList supports duplicacy of entries.
So what I did to make commonList unique is I introduced a HashMap object as shown below:
getCommonsArrayList(listA:ArrayList< User >, listB:ArrayList<User>):ArrayList<User>{
var listCommon = ArrayList<User>()
var arrResponseMap = HashMap<String,User>()
for (i in listA.indices) {
for (j in listB.indices) {
if (listA[i].id.equals(listB[j].id)) { //if id of the user matches
arrResponseMap.put(listA[i].id,listA[i]) // add id to map so there would be no duplicacy
}
}
}
arrResponseMap.forEach {
listCommon.add(it.value) //iterate the map and add all values
}
return listCommon // return the new list with common entries
}
This will give the new arrayList of userObject with common Id's. But this has an increased complexity than the above code.
If the size of the listA and listB increases to 1000 then this execution will take heavy time.
Can someone guide me if there is some better way to solve this.
You can simply use distinctBy to get only unique values from list.
Official Doc:
Returns a sequence containing only elements from the given sequence
having distinct keys returned by the given selector function.
The elements in the resulting sequence are in the same order as they
were in the source sequence.
Here is an example:
val model1 = UserModel()
model1.userId = 1
val model2 = UserModel()
model1.userId = 2
val model3 = UserModel()
model1.userId = 1
val model4 = UserModel()
model1.userId = 2
val commonList = listOf(model1, model2, model3, model4)
// get unique list based on userID, use any field to base your distinction
val uniqueList = commonList
.distinctBy { it.userId }
.toList()
assert(uniqueList.count() == 2)
assert(commonList.count() == 4)
Add the both the list and use distinctBy like this
data class DevelopersDetail(val domain: String, val role: String)
val d1 = DevelopersDetail("a", "1")
val d2 = DevelopersDetail("b", "1")
val d3 = DevelopersDetail("c", "1")
val d4 = DevelopersDetail("c", "1")
val d5 = DevelopersDetail("d", "1")
var listA = listOf(d1, d2, d3, d4)
var listb = listOf(d1, d2, d3, d4)
var data = listA + listb
var list= data
.distinctBy { it.domain }
.toList()
println("list $list")
//output-list [DevelopersDetail(domain=a, role=1), DevelopersDetail(domain=b, role=1), DevelopersDetail(domain=c, role=1)]

How to sort a list by self defined order?

I want to use Groovy to sort following list by a defined order
def ls = [ 'sperson_firstname', 'a_id', 'a_name', 'scontact_street', 'scontact_email', 'sperson_nationality', 'scontact_tel', 'sperson _birthday', 'sperson_lastname', 'scontact_fax']
After sort should be sorted like this:
ls = ['a_id', 'a_name', 'sperson_firstname', 'sperson_lastname', 'sperson _birthday','sperson_nationality','scontact_street', 'scontact_email', 'scontact_tel', 'scontact_fax']
that means my defined order should sort prefix first like
[a , sperson, scontact]
Then for each prefix should sort its suffix with a defined order
e.g. for prefix sperson should sort like
['sperson_firstname', 'sperson_lastname', 'sperson _birthday','sperson_nationality']
for prefix scontact should sort like
['scontact_street', 'scontact_email', 'scontact_tel', 'scontact_fax']
I have tried
def sortOrder = ["a","sperson","scontact"]
ls.sort{ls -> sortOrder.indexOf(ls.split("_")[0]) }
but this can only solve the prefix sorting ...
You can do what you are doing and extend it for the prefix:
List prefixOrder = ['a' , 'sperson', 'scontact']
Map suffixOrdersByPrefix = [
a: [...],
b: [...],
c: [...]
]
def indexOfComparator = { list, a, b ->
list.indexOf(a).compareTo(list.indexOf(b))
}
def prefixComparator = { a, b ->
indexOfComparator(prefixOrder, a, b)
}
def suffixComparator = { prefix, a, b ->
indexOfComparator(suffixOrdersByPrefix[prefix], a, b)
}
l.sort { a, b ->
List<String> aTokens = a.tokenize('_'),
bTokens = b.tokenize('_')
prefixComparator(aTokens.first(), bTokens.first()) ?:
suffixComparator(aTokens.first(), aTokens.last(), bTokens.last())
}
You compare the prefixes, if there are equal (i.e comparation returns 0) you compare the suffixes. You can map the order per suffix using... well, a map :).
Your approach wasn't that far off. I just used a two-dimensional array (actually a list of lists I guess), where the first element is the prefix and the following are the suffixes for that index. Then the order is just a weighted prefix score plus the suffix score.
def ls = ['sperson_firstname', 'a_id', 'a_name', 'scontact_street', 'scontact_email', 'sperson_nationality', 'scontact_tel', 'sperson_birthday', 'sperson_lastname', 'scontact_fax']
def sortOrder = [
['a','id','name'],
['sperson','firstname','lastname','birthday','nationality'],
['scontact','street','email','tel','fax']
]
ls.sort{item ->
def preIndex = sortOrder.collect{it[0]}.indexOf(item.split('_')[0])
def postIndex = sortOrder[preIndex][1..(sortOrder[preIndex].size-1)].indexOf(item.split('_')[1])
return (preIndex * 10) + postIndex
}
assert ['a_id', 'a_name', 'sperson_firstname', 'sperson_lastname', 'sperson_birthday', 'sperson_nationality', 'scontact_street', 'scontact_email', 'scontact_tel', 'scontact_fax'] == ls

Linq: Group on list inside lists

Let's say I have a list of lists.
For each item in this list , I have a list of custom objects.
These objects are as such:
public string Field1
public string Field2
public string Field3
What I'd like to achieve through Linq: filter out of my list of lists all the objects which have the same three fields, which are not the first element of their list and keep only the first one.
So let's say I have two lists listA and list B in my list.
listA has three objects object1, object2 and object3.
object1.Field1 = "a" object1.Field2 = "A" object1.Field3 = "1"
object2.Field1 = "a" object2.Field2 = "B" object2.Field3 = "2"
object3.Field1 = "a" object3.Field2 = "C" object3.Field3 = "3"
listB has three objects object4, object5 and object6.
object4.Field1 = "a" object4.Field2 = "A" object4.Field3 = "1"
object5.Field1 = "a" object5.Field2 = "B" object5.Field3 = "2"
object6.Field1 = "a" object6.Field2 = "D" object6.Field3 = "3"
In this example, object1 and object4 are the same, but because they are first in their respective list, they are not filtered out.
However, object2 and object5 having the same three fields values, only one of them will be kept so that at then end of my process, I'll have my two list like so:
listA has three objects object1, object2 and object3.
object1.Field1 = "a" object1.Field2 = "A" object1.Field3 = "1"
object2.Field1 = "a" object2.Field2 = "B" object2.Field3 = "2"
object3.Field1 = "a" object3.Field2 = "C" object3.Field3 = "3"
listB has now two objects object4 and object6.
object4.Field1 = "a" object4.Field2 = "A" object4.Field3 = "1"
object6.Field1 = "a" object6.Field2 = "D" object6.Field3 = "3"
I've been scratching my head for hours about this to no avail. I cannot do a foreach list of lists look into all the other lists as it would cause a performance problem (I can potentially have 1000000 lists of lists).
Anyone with an idea for this?
Why does it have to be LINQ? A simple iterator block solves the problem quite nicely.
The code below assumes you have overridden Equals and GetHashCode in your object to check for equality on the 3 fields. If that is not possible, use a custom equality comparer instead (to be passed in the HashSet constructor)
static IEnumerable<List<T>> GetFilteredList<T>(IEnumerable<List<T>> input)
{
var found = new HashSet<T>();
foreach (var list in input)
{
var returnList = new List<T>(list.Capacity);
foreach (var item in list)
{
// the first item is always added
// any other items are only added if they were
// never encountered before
if (list.Count == 0 || !found.Contains(item))
{
found.Add(item);
returnList.Add(item);
}
}
yield return returnList;
}
}
If you can stick with IEnumerable<IEnumerable<T>> as the return value, another approach that only sweeps the input just once could be something like this (without creating intermediary lists):
static IEnumerable<IEnumerable<T>> GetFilteredList<T>(IEnumerable<List<T>> input)
{
var encounteredElements = new HashSet<T>();
foreach (var list in input)
{
yield return Process(list, encounteredElements);
}
}
static IEnumerable<T> Process<T>(IEnumerable<T> input,
HashSet<T> encounteredElements)
{
bool first = true;
foreach (var item in input)
{
if (first) yield return item;
if (!encounteredElements.Contains(item))
{
yield return item;
}
encounteredElements.Add(item);
first = false;
}
}

How to sort a lookup?

Hi I have a lookup type that stores strings and ints.
static Lookup<string, int> lookup;
lookup = (Lookup<string, int>)list.ToLookup(i => i.IP, i => i.Number);
But now I need to sort this lookup by the values (number), and get the top 10 keys with their values.
How is this possible?
Unfortunately elements inside a Lookup cannot be reordered.
But the ToLookup() method has a nice property that elements in all the groupings have the same order as the elements in the original sequence.
This means that with some Linq gymnastics, you can achieve what you want by using GroupBy:
var l = (from l in list
// group elements by key
group l by l.IP into g
// for each group order the elements and take top 10
select new { g.Key, Items = g.OrderBy(g1 => g1.Number).Take(10)} into g2
// flaten group into an enumerable using select many
from g in g2.Items
select g)
// get the desired lookup containing the top 10 ordered elements for each key
.ToLookup(g => g.IP, g => g.Number);
I'm not sure why you are casting a Lookup<string, int> to a Lookup<string, string>, but the general answer you want is:
var list = new List<Test>
{
new Test { IP = "A", Number = 1 }, new Test { IP = "A", Number = 3 }, new Test { IP = "A", Number = 4 },
new Test { IP = "B", Number = 1 }, new Test { IP = "B", Number = 1 }, new Test { IP = "B", Number = 1 },
new Test { IP = "C", Number = 1 },
new Test { IP = "D", Number = 1 },
new Test { IP = "E", Number = 1 }, new Test { IP = "E", Number = 1 }, new Test { IP = "E", Number = 1 }
};
var values = list.ToLookup(s => s.IP, s => s.Number)
.OrderByDescending(s => s.Count())
.Take(10);
Go find a Priority Queue (you can find one at http://www.itu.dk/research/c5/). Iterate over your look up and insert an IComparable item created from each entry in the look up, into the priority queue. Select the top ten items from the priority queue. Or just sort them by the count as the key.
var lookup = list.ToLookup( l => l.IP, l => l.Number );
var topten = lookup.OrderByDescending( l => l.Count() )
.Take( 10 );
foreach (var item in topten)
{
Console.WriteLine( "{0}: {1}", item.Key, item.Count() );
}
Note that sorting will have at best O(nlogn) performance while a good, heap-based priority queue will have O(logn) performance. If the collection isn't large, sorting is simpler given the built in support for it and not needing an intermediate class to support the priority queue implementation.
Take a look at the Take() LINQ function you should be able to do something like Take(10) to just return 10 results. As for sorting, check out the OrderBy() function that accepts a lambda expression as a sorting mechanism. Combining them both should give you what you're after.

Linq Query - Finding consecutive increasing values

Say I have the following class:
internal class ModuleScrap
{
public System.DateTime ReadTime { get; set; }
public int NetScrap { get; set; }
}
I would like a Linq query that finds for me all NetScrap values that were greater than the NetScrap value before it, based on ReadTime. So, a query that looks something like this:
MyList
.OrderBy(row => row.ReadTime)
.Where (row => row.NetScrap > [The Previous NetScrap Value])
Is such a query possible?
Yes, using Zip and Skip (assuming .NET 4):
// Avoid having to do the ordering twice
var ordered = list.OrderBy(row => row.ReadTime).ToList();
var greater = ordered.Zip(ordered.Skip(1), (x, y) => new { x, y })
.Where(p => p.y.NetScrap > p.x.NetScrap)
.Select(p => p.y);
Zipping a sequence with itself skipped one gives you pairs of consecutive elements:
Original a b c d e f
Original.Skip(1) b c d e f g
If you read each column from the above, you get the pairings. From there, you just need to select each value where the second entry's NetScrap is greater than the first.

Resources