Get indexes of all occurrences of a substring within a string - rust

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")]);

Related

Clean way to collect into matrix

I'd like to split and collect this strangely-shaped vector
let v = vec![
0, 1, 2, 3,
4, 5, 6, 7,
8, 9,
10, 11,
12, 13,
];
into these two matrices:
let v1 = vec![
vec![0, 1, 2, 3],
vec![4, 5, 6, 7],
];
let v2 = vec![
vec![8, 9],
vec![10, 11],
vec![12, 13],
];
(The elements are sequential (i.e. 1, 2, 3, ...) but this is just for example. Also, though the number of matrices are two here, this number is just for example; sometimes it should be three or more.)
Trivially it is possible (Rust Playground):
let mut v1: Vec<Vec<usize>> = vec![];
for i in 0..2 {
v1.push(v.iter().skip(i * 4).take(4).copied().collect());
}
let mut v2: Vec<Vec<usize>> = vec![];
for i in 0..3 {
v2.push(v.iter().skip(8 + i * 2).take(2).copied().collect());
}
But, is there a cleaner way? Here's the pseudo code I want:
let v1 = v.iter().every(4).take(2).collect();
let v2 = v.iter().skip(8).every(2).take(3).collect();
You can split the initial vector into two slices and iterate each of them separately (playground):
let (left, right) = v.split_at(8);
let v1 = left.chunks(4).map(|s| s.to_vec()).collect::<Vec<_>>();
let v2 = right.chunks(2).map(|s| s.to_vec()).collect::<Vec<_>>();
If an external crate is allowed, you can use Itertools::chunks:
v.iter().chunks(4).into_iter().take(2).map(|l| l.copied().collect_vec()).collect()
(Rust Playground)

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

Sorting a vector based on frequency and position of the elements in Rust

I have a vector and I want to sort it, where the first criterion is frequency. Second criterion is position in the vector. If two elements have the same number of occurrences, I want the most recently seen element to take advantage and go first. In the end, I want to remove duplicate elements from it.
For instance, if the input is this:
fn main() {
let history = vec![3, 2, 4, 6, 2, 4, 3, 3, 4, 5, 6, 3, 2, 4, 5, 5, 3];
}
The output should be:
3 4 5 2 6
How can I do this in Rust?
A straightforward method is to build hash maps for frequencies and positions of the elements:
use std::collections::HashMap;
fn frequency_map(nums: &[i32]) -> HashMap<i32, usize> {
let mut map = HashMap::new();
for &n in nums {
*map.entry(n).or_insert(0) += 1;
}
map
}
fn position_map(nums: &[i32]) -> HashMap<i32, usize> {
let mut map = HashMap::new();
for (pos, &n) in nums.iter().enumerate() {
map.insert(n, pos);
}
map
}
And then do an unstable sort by position followed by a stable sort by frequency:
fn custom_sort(nums: &mut Vec<i32>) {
let freq_map = frequency_map(nums);
let pos_map = position_map(nums);
nums.sort_unstable_by(|a, b| pos_map.get(b).unwrap().cmp(pos_map.get(a).unwrap()));
nums.dedup();
nums.sort_by(|a, b| freq_map.get(b).unwrap().cmp(freq_map.get(a).unwrap()));
}
Example:
use itertools::Itertools;
fn main() {
let mut history = vec![3, 2, 4, 6, 2, 4, 3, 3, 4, 5, 6, 3, 2, 4, 5, 5, 3];
custom_sort(&mut history);
println!("[{}]", history.iter().format(", "));
}
Output:
[3, 4, 5, 2, 6]
(playground)

How to concatenate the individual elements of a vector of slices with other slices?

I have a slice of bytes start = [30u8; 5] and middle = [40u8; 3] and a vector of byte slices:
let first = [1u8; 10];
let second = [2u8; 10];
let third = [3u8; 10];
let elements: Vec<[u8; 10]> = vec![first, second, third];
I want to concatenate everything together, in such a way that I will obtain a single byte slice which looks as
[30, 30, 30, 30, 30, 40, 40, 40, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
However, although I can concatenate start and middle when I try to append the vector elements it fails. I know that I am wrongly trying to iterate through the elements of the vector to concatenate, but I can't figure out how to do it correctly?
fn main() {
let start = [30u8; 5];
let middle = [40u8; 4];
let first = [1u8; 10];
let second = [2u8; 10];
let third = [3u8; 10];
let elements: Vec<[u8; 10]> = vec![first, second, third];
println!("{:?}", elements.iter());
for key in elements.iter() {
println!("{:?}", key.iter());
}
let alltogether: Vec<u8> = start
.iter()
.cloned()
.chain(middle.iter().cloned())
.chain(elements.iter().iter().cloned())
.collect();
println!("{:?}", alltogether);
}
This example can be copy-pasted into the Rust playground.
You possibly want this:
let alltogether: Vec<u8> = start
.iter()
.cloned()
.chain(middle.iter().cloned())
.chain(elements.iter().flatten().cloned())
.collect();
Note that there is also copied (instead of cloned) that can be used for Copyable types.
If the stuff in elements does not implement IntoIterator itself, you can use flat_map to specify how to convert one element to an iterator.

Resources