How is ordering defined for tuples in Rust? - rust

I was looking at the documentation and found an example code that looked unfamiliar.
std::cmp::Reverse - Rust
use std::cmp::Reverse;
let mut v = vec![1, 2, 3, 4, 5, 6];
v.sort_by_key(|&num| (num > 3, Reverse(num)));
assert_eq!(v, vec![3, 2, 1, 6, 5, 4]);
How does (num > 3, Reverse(num)) define ordering between themselves?
I had a look into documentation for tuple, and it said
The sequential nature of the tuple applies to its implementations of various traits. For example, in PartialOrd and Ord, the elements are compared sequentially until the first non-equal set is found.
That makes sense for equality checks, but it seems to me that it does not give explanation for how > and < acts on tuples.
I did some experiments, but understood nothing.
println!("{}", (5, 5) > (3, 4)); // true
println!("{}", (2, 2) > (3, 4)); // false
println!("{}", (2, 5) > (3, 4)); // false
println!("{}", (3, 5) > (3, 4)); // true
println!("{}", (5, 2) > (3, 4)); // true

As what you quoted notes, tuples are compared lexicographically.
That is, the first elements of each tuple are compared, then if they're equal the second elements are, then the third, etc.. until a non-equal pair is found and provides the ordering of the tuples. If all pairs are equal then the tuples are, obviously, equal.
println!("{}", (5, 5) > (3, 4)); // true
5 > 3, therefore (5, _) > (3, _)
println!("{}", (2, 2) > (3, 4)); // false
2 < 3, therefore (2, _) < (3, _)
println!("{}", (2, 5) > (3, 4)); // false
see above
println!("{}", (3, 5) > (3, 4)); // true
3 == 3, 5 > 4, therefore (3, 5) > (3, 4)
println!("{}", (5, 2) > (3, 4)); // true
see first case
How does (num > 3, Reverse(num)) define ordering between themselves?
booleans sort false < true, therefore it first orders the elements in two broad categories (numbers below 3 then numbers above 3) then within each category items are ordered based on their reverse natural order (that is, largest-first). Though it obviously does that in a single pass.

You can read the source code of tuple:
impl<$($T:PartialOrd + PartialEq),+> PartialOrd for ($($T,)+)
where last_type!($($T,)+): ?Sized {
#[inline]
fn partial_cmp(&self, other: &($($T,)+)) -> Option<Ordering> {
lexical_partial_cmp!($(self.$idx, other.$idx),+)
}
// ...
#[inline]
fn gt(&self, other: &($($T,)+)) -> bool {
lexical_ord!(gt, $(self.$idx, other.$idx),+)
}
}
And the lexical_ord macro:
// Constructs an expression that performs a lexical ordering using method $rel.
// The values are interleaved, so the macro invocation for
// `(a1, a2, a3) < (b1, b2, b3)` would be `lexical_ord!(lt, a1, b1, a2, b2,
// a3, b3)` (and similarly for `lexical_cmp`)
macro_rules! lexical_ord {
($rel: ident, $a:expr, $b:expr, $($rest_a:expr, $rest_b:expr),+) => {
if $a != $b { lexical_ord!($rel, $a, $b) }
else { lexical_ord!($rel, $($rest_a, $rest_b),+) }
};
($rel: ident, $a:expr, $b:expr) => { ($a) . $rel (& $b) };
}
So (a, b) > (c, d) will call (a, b).gt(&(c, d)), which will use the lexical_ord macro like this (see comment in the code):
lexical_ord(gt, a, c, b, d)
(Actually, it should be something like lexical_ord(gt, (a, b).0, (c, d).0, (a, b).1, (c, d).1), if I'm reading the macro correctly, but I've simplified it here.)
Which will be translated (at compile-time) to this:
if a != c {
(a).gt(&c)
} else {
(b).gt(&d)
}
So the actual code that will be called for (a, b) > (c, d) will be:
fn gt(&self, other: &($T, $T)) -> bool {
if self.0 != other.0 {
(self.0).gt(&other.0) // self.0 > other.0
} else {
(self.1).gt(&other.1) // self.1 > other.1
}
}
So it's comparing the values in each tuple one-by-one in pairs.

That makes sense for equality checks, but it seems to me that it does not give explanation for how > and < acts on tuples.
It does, consider the examples you gave:
println!("{}", (5, 5) > (3, 4)); // 5 > 3 is true
println!("{}", (2, 2) > (3, 4)); // 2 > 3 is false
println!("{}", (2, 5) > (3, 4)); // 2 > 3 is false
println!("{}", (3, 5) > (3, 4)); // 3 == 3, then: 5 > 4 is true
println!("{}", (5, 2) > (3, 4)); // 5 > 3 is true
It returns the result of </> of the first non-equal element of the tuple.

Related

Calling variadic function with iterator in Rust

A variadic function takes a variable number of arguments of same type. Macro function !product from Itertools package are one of those and I'd like to compute it with a vector of ranges.
Example usage of !product is following where each argument is a Range
use itertools::iproduct;
// Iterate over the coordinates of a 4 x 4 x 4 grid
// from (0, 0, 0), (0, 0, 1), .., (0, 1, 0), (0, 1, 1), .. etc until (3, 3, 3)
for (i, j, k) in iproduct!(0..4, 0..4, 0..4) {
// ..
}
How can I call iproduct with an iterator or a vector where each element will become each argument into iproduct? For instance
use std::ops::Range;
let ranges : Vec<Range<i64>> = vec![
Range {start: 5, end: 10},
Range {start: 0, end: 10},
Range {start: -2, end: 3}
];
// How to call here properly??
for (i, j, k) in iproduct!(ranges) {
// ..
}
Side note:
For instance in python, one would call the function using the star sign before the variable: product(*ranges).
I think you're looking for Itertools::multi_cartesian_product
use itertools::Itertools;
use std::ops::Range;
fn main() {
let ranges: Vec<Range<i64>> = vec![
5..10,
0..10,
-2..3,
];
// How to call here properly??
for v in ranges.into_iter().multi_cartesian_product() {
let i = v[0];
let j = v[1];
let k = v[2];
// ..
}
}

How to convert BTreeSet to BTreeMap?

I have a BTreeSet object and I want to convert it into a BTreeMap with default indices (i.e., starting from 0 and incrementing). More precisely, given this:
let set = BTreeSet::from([0, 2, 4, 6, 8]);
I want to end up with BTreeMap<usize, usize> that holds [(0, 0), (1, 2), (2, 4), (3, 6), (4, 8)]. I can do it with a loop for example as
let mut tree = BTreeMap::new();
for (i, v) in set.clone().into_iter().enumerate() {
tree.insert(i, v);
}
But is there any shortcut (i.e., one line code) that does the same?
But is there any shortcut (i.e., one line code) that does the same?
let map = set.iter().copied().enumerate().collect::<BTreeMap<_, _>>();
println!("{:?}", map);
{0: 0, 1: 2, 2: 4, 3: 6, 4: 8}
tho note that this is a BTreeMap<usize, i32>, because i32 is the default resolution for {integer} literals if they're otherwise unconstrained, which is the case here.

Sort a hashmap by values in rust

In python its done this way:
>>> x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
>>> {k: v for k, v in sorted(x.items(), key=lambda item: item[1])}
{0: 0, 2: 1, 1: 2, 4: 3, 3: 4}
How to sort a HashMap by values in rust?
My code so far:
use std::collections::HashMap;
fn main() {
let mut count: HashMap<String, u32>= HashMap::new();
count.insert(String::from("A"), 5);
count.insert(String::from("B"), 2);
count.insert(String::from("C"), 11);
count.insert(String::from("D"), 10);
let highest = count.iter().max_by(|a, b| a.1.cmp(&b.1)).unwrap();
println!("largest hash: {:?}", highest); // largest hash: ("C", 11)
}
Unlike Python's dict, Rust's "built-in" hashmap is not ordered, so sorting it has no effect.
If you need an ordered map for some reason, you should use indexmap. Alternatively, BTreeMap is sorted based on the key.
As you don't really present any sort of compelling use case it's hard to provide counsel though.
Ya, sorted it by converting to vector:
use std::collections::HashMap;
fn main() {
let mut count: HashMap<String, u32>= HashMap::new();
count.insert(String::from("A"), 5);
count.insert(String::from("B"), 2);
count.insert(String::from("C"), 11);
count.insert(String::from("D"), 10);
let mut hash_vec: Vec<(&String, &u32)> = count.iter().collect();
println!("{:?}", hash_vec);
hash_vec.sort_by(|a, b| b.1.cmp(a.1));
println!("Sorted: {:?}", hash_vec); //Sorted: [("C", 11), ("D", 10), ("A", 5), ("B", 2)]
}
Sort HashMap data by value

Get indexes of all occurrences of a substring within a string

If I wanted to get the index of the first occurrence of, say, substring "foo" within a string "foo bar foo baz foo", I'd use:
fn main() {
let my_string = String::from("foo bar foo baz foo");
println!("{:?}", my_string.find("foo"));
}
...which would give me Some(0).
However, I need to find indexes of all occurrences of a substring within a string.
In this scenario, I'd need something like:
[0, 8, 16]
How can I do this idiomatically in Rust?
Use match_indices. Example from Rust docs:
let v: Vec<_> = "abcXXXabcYYYabc".match_indices("abc").collect();
assert_eq!(v, [(0, "abc"), (6, "abc"), (12, "abc")]);
let v: Vec<_> = "1abcabc2".match_indices("abc").collect();
assert_eq!(v, [(1, "abc"), (4, "abc")]);
let v: Vec<_> = "ababa".match_indices("aba").collect();
assert_eq!(v, [(0, "aba")]); // only the first `aba`
I think the most complete answer, based on the OP's requirement, would be:
let v: Vec<_> = "abcXXXabcYYYabc".match_indices("abc").map(|(i, _)|i).collect();
assert_eq!(v, [0,6,12]);
There is a match_indices: https://doc.rust-lang.org/std/string/struct.String.html#method.match_indices
let v: Vec<_> = "abcXXXabcYYYabc".match_indices("abc").collect();
assert_eq!(v, [(0, "abc"), (6, "abc"), (12, "abc")]);

How to do Groupby and map in one go in python just like we do in scala

we have a list in Scala :
val testList = List("Sita" -> 1, "Sita" -> 2, "Ram" -> 3, "Ram" -> 4, "Shyam" -> 5)
I applied this on list to group the values by key in Scala:
val res = testList.groupBy(_._1).map { case (k, v) => k -> v.map(_._2).sum }
and i got this as a result:
Map(Shyam -> 5, Ram -> 7, Sita -> 3)
I want to do the same in python Please help me out:
testList = [("Sita", 1), ("Sita", 2), ("Ram", 3), ("Ram", 4), ("Shyam", 5)]
Edit: And what if i have a list like this:
testList = [("Sita_English", 1), ("Sita_Maths", 2), ("Ram_English", 3), ("Ram_Maths", 4), ("Shyam_English", 5)]
testList = [("Sita", 1), ("Sita", 2), ("Ram", 3), ("Ram", 4), ("Shyam", 5)]
from itertools import groupby
out = {v: sum(i[1] for i in g) for v, g in groupby(testList, lambda k: k[0])}
print(out)
Prints:
{&apos;Sita&apos;: 3, &apos;Ram&apos;: 7, &apos;Shyam&apos;: 5}
A little explanation:
itertools.groupby() (doc) returns consecutive keys and groups from the iterable. In this case the iterable is tesList. The key function lambda k: k[0] returns first element of the tuple -> so we are grouping along the first element.
The dict comprehension is using this first element as a key and second elements from the group as parameter to the sum() function.
EDIT: Using only map():
testList = [("Sita", 1), ("Sita", 2), ("Ram", 3), ("Ram", 4), ("Shyam", 5)]
from itertools import groupby
from operator import itemgetter
out = dict(map(lambda v:(v[0], sum(map(itemgetter(1), v[1]))), groupby(testList, itemgetter(0))))
print(out)
I think you want to have a solution in functional way:
out = list(map(lambda v:(v[0], sum(map(lambda s: s[1], v[1]))), groupby(testList, key=lambda x: x[0])))

Resources