Kotin sort by descending then ascending - sorting

Im trying to order a list on multiple parameters.. for example, one value descending, second value ascending, third value descending.
var people = listOf<People>(People("palazo", "ooo", 1),
People("asdf", "cccc", 2),
People("Maria", "ooo", 3),
People("Helena", "ccccc", 3),
People("Carlos", "ccc", 4),
People("Jhon", "ooo", 2)
)
is there a way like this to do it? (i know is incorrect)
people = people.sortedByDescending { it.name}.thenBy{it.lastname}.thenDescending{it.age}
or add to :
people.sortedWith(compareBy(People::name, People::lastName))
//but mixing ascending and descending

val sortedPeople = people.sortedWith(
compareByDescending(People::name)
.thenBy(People::lastName)
.thenByDescending(People::age)
)

Related

Cartesian product with specific order

I need to output cartesian product of N lists in specific order.
I know how to build products in "default" order:
Given sets (a, b, c), (x, y), (1, 2, 3), first I produce ax1, then iterate over last set to get ax2, ax3, then change element in the second set and iterate over the last set again for ay1, ay2, ay3, etc...
The order I need should not go for the N-th element in any set, before producing products of N-1 elements
Desired result is ax1, ax2, ay1, ay2, bx1, bx2, by1, by2, ax3, ay3, bx3, by3, cx1, cx2, cx3, cy1, cy2, cy3. See, I don't get ax3 (containing 3rd element from (1, 2, 3)), before producing all products with 2nd elements.
My current algorithm is:
trunace sets to length 1
generate products
truncate sets to length 2
generate products
remove duplicates, preserving order
...
truncate sets to length max length of all sets
generate products
remove duplicates, preserving order
Each step "generate products" also generates all products from the previous step, so I have to remove them
Is it the better algorith to get desired order? Does it have a name?
Not sure if this order has a name, but this seems to do what you ask for without having to remove repeated items.
from itertools import islice, product, zip_longest
def specific_order_cartesian(lists):
its = [[lst[0]] for lst in lists]
yield tuple(lst[0] for lst in lists)
for column in list(islice(zip_longest(*lists), 1, None)):
for i, p in reversed(list(enumerate(column))):
if p is None:
continue
yield from product(
*(
(p,) if j == i else its[j]
for j in range(len(lists))
)
)
its[i].append(p)
print(list(specific_order_cartesian(
[('a', 'b', 'c',), ('x', 'y'), (1, 2, 3)]
)))

Sort list of tuples based on second element while alternating over third element

Given a list of tuples:
val mylist = List(('orange', 0.9, 10), ('apple', 0.8, 10), ('mellon', 0.7, 10),
('car', 0.5, 2), ('truck', 0.5, 2),('tablet', 0.3, 3))
I would like to sort them in descending order with respect to the second element of the tuple. However, I would like to pick them by category, one at a time (third element) alternatively. The output should be the following list:
('orange', 0.9, 10)
('car', 0.5, 2)
('tablet', 0.3, 3)
('apple', 0.8, 10)
('truck', 0.5, 2)
('mellon', 0.7, 10)
What would be the functional way of doing it in Scala?
PS: Notice that the the result must reflect the ordering of the third element, that is, 10 appears before 2, and 2 before 3.
You can do this by adding two indices to each item in the list:
The item's category position among all existing categories (i.e. 0 for category 10, 1 for 2 and 2 for 3)
The item's index within its category by descending value of the second tuple element
Once these are added, you can order by them both (with the index withn the category taking precedence)
// traverse once to get a map of Category -> CategoryIndex
val categoryOrder: Map[Int, Int] = mylist.map(_._3).distinct.zipWithIndex.toMap
val result: List[(String, Double, Int)] = mylist
.groupBy(_._3).mapValues(_.sortBy(-_._2).zipWithIndex) // add the index of each item within its category
.toList.flatMap(_._2) // remove group keys and flatMap to get back to tuples
.sortBy { case (t, indx) => (indx, categoryOrder(t._3)) } // order by index in category and category index
.map(_._1) // remove indices
println(result)
// List((orange,0.9,10), (car,0.5,2), (tablet,0.3,3), (apple,0.8,10), (truck,0.5,2), (mellon,0.7,10))

How to make this sparse matrix and trie work in tandem

I have a sparse matrix that has been exported to this format:
(1, 3) = 4
(0, 5) = 88
(6, 0) = 100
...
Strings are stored into a Trie data structure. The numbers in the previous exported sparse matrix correspond to the result of the lookup on the Trie.
Lets say the word "stackoverflow" is mapped to number '0'. I need to iterate the exported sparse matrix where the first element is equals to '0' and find the highest value.
For example:
(0, 1) = 4
(0, 3) = 8
(0, 9) = 100 <-- highest value
(0, 9) is going to win.
What would be the best implementation to store the exported sparse matrix?
In general, what would be the best approach (data structure, algorithm) to handle this functionality?
Absent memory or dynamism constraints, probably the best approach is to slurp the sparse matrix into a map from first number to the pairs ordered by value, e.g.,
matrix_map = {} # empty map
for (first_number, second_number, value) in matrix_triples:
if first_number not in matrix_map:
matrix_map[first_number] = [] # empty list
matrix_map[first_number].append((second_number, value))
for lst in matrix_map.values():
lst.sort(key=itemgetter(1), reverse=True) # sort by value descending
Given a matrix like
(0, 1) = 4
(0, 3) = 8
(0, 5) = 88
(0, 9) = 100
(1, 3) = 4
(6, 0) = 100,
the finished product looks like this:
{0: [(9, 100), (5, 88), (3, 8), (1, 4)],
1: [(3, 4)],
6: [(0, 100)]}.

top down ranges merge?

I want to merge some intervals like this:
>>> ranges = [(30, 45), (40, 50), (10, 50), (60, 90), (90, 100)]
>>> merge(ranges)
[(10, 50), (60, 100)]
I'm not in cs field. I know how to do it by iteration, but wonder if there's a more efficient "top-down" approach to merge them more efficiently, maybe using some special data structure?
Thanks.
Interval tree definitely works, but it is more complex than what you need. Interval tree is an "online" solution, and so it allows you to add some intervals, look at the union, add more intervals, look again, etc.
If you have all the intervals upfront, you can do something simpler:
Start with the input
ranges = [(30, 45), (40, 50), (10, 50)]
Convert the range list into a list of endpoints. If you have range (A, B), you'll convert it to two endpoints: (A, 0) will be the left endpoint and (B, 1) wil be the right endpoint.
endpoints = [(30, 0), (45, 1), (40, 0), (50, 1), (10, 0), (50, 1)]
Sort the endpoints
endpoints = [(10, 0), (30, 0), (40, 0), (45, 1), (50, 1), (50, 1)]
Scan forward through the endpoints list. Increment a counter when you see a left endpoint and decrement the counter when you see a right endpoint. Whenever the counter hits 0, you close the current merged interval.
This solution can be implemented in a few lines.
Yeah, the efficient way to do it is to use an interval tree.
The following algorithm in C# does what you want. It uses DateTime interval ranges, but you can adapt it however you like. Once the collection is sorted in ascending start order, if the start of the next interval is at or before the end of the previous one, they overlap, and you extend the end time outward if needed. Otherwise they don't overlap, and you save the prior one off to the results.
public static List<DateTimeRange> MergeTimeRanges(List<DateTimeRange> inputRanges)
{
List<DateTimeRange> mergedRanges = new List<DateTimeRange>();
// Sort in ascending start order.
inputRanges.Sort();
DateTime currentStart = inputRanges[0].Start;
DateTime currentEnd = inputRanges[0].End;
for (int i = 1; i < inputRanges.Count; i++)
{
if (inputRanges[i].Start <= currentEnd)
{
if (inputRanges[i].End > currentEnd)
{
currentEnd = inputRanges[i].End; // Extend range.
}
}
else
{
// Save current range to output.
mergedRanges.Add(new DateTimeRange(currentStart, currentEnd));
currentStart = inputRanges[i].Start;
currentEnd = inputRanges[i].End;
}
}
mergedRanges.Add(new DateTimeRange(currentStart, currentEnd));
return mergedRanges;
}

How to generate cross product of sets in specific order

Given some sets (or lists) of numbers, I would like to iterate through the cross product of these sets in the order determined by the sum of the returned numbers. For example, if the given sets are { 1,2,3 }, { 2,4 }, { 5 }, then I would like to retrieve the cross-products in the order
<3,4,5>,
<2,4,5>,
<3,2,5> or <1,4,5>,
<2,2,5>,
<1,2,5>
I can't compute all the cross-products first and then sort them, because there are way too many. Is there any clever way to achieve this with an iterator?
(I'm using Perl for this, in case there are modules that would help.)
For two sets A and B, we can use a min heap as follows.
Sort A.
Sort B.
Push (0, 0) into a min heap H with priority function (i, j) |-> A[i] + B[j]. Break ties preferring small i and j.
While H is not empty, pop (i, j), output (A[i], B[j]), insert (i + 1, j) and (i, j + 1) if they exist and don't already belong to H.
For more than two sets, use the naive algorithm and sort to get down to two sets. In the best case (which happens when each set is relatively small), this requires storage for O(√#tuples) tuples instead of Ω(#tuples).
Here's some Python to do this. It should transliterate reasonably straightforwardly to Perl. You'll need a heap library from CPAN and to convert my tuples to strings so that they can be keys in a Perl hash. The set can be stored as a hash as well.
from heapq import heappop, heappush
def largest_to_smallest(lists):
"""
>>> print list(largest_to_smallest([[1, 2, 3], [2, 4], [5]]))
[(3, 4, 5), (2, 4, 5), (3, 2, 5), (1, 4, 5), (2, 2, 5), (1, 2, 5)]
"""
for lst in lists:
lst.sort(reverse=True)
num_lists = len(lists)
index_tuples_in_heap = set()
min_heap = []
def insert(index_tuple):
if index_tuple in index_tuples_in_heap:
return
index_tuples_in_heap.add(index_tuple)
minus_sum = 0 # compute -sum because it's a min heap, not a max heap
for i in xrange(num_lists): # 0, ..., num_lists - 1
if index_tuple[i] >= len(lists[i]):
return
minus_sum -= lists[i][index_tuple[i]]
heappush(min_heap, (minus_sum, index_tuple))
insert((0,) * num_lists)
while min_heap:
minus_sum, index_tuple = heappop(min_heap)
elements = []
for i in xrange(num_lists):
elements.append(lists[i][index_tuple[i]])
yield tuple(elements) # this is where the tuple is returned
for i in xrange(num_lists):
neighbor = []
for j in xrange(num_lists):
if i == j:
neighbor.append(index_tuple[j] + 1)
else:
neighbor.append(index_tuple[j])
insert(tuple(neighbor))

Resources