I'm trying to match on an array in Rust:
let s = "x,y,z";
let v: Vec<_> = s.split(',').collect();
let (x, y, z) = match &v[..] {
[x, y, z] => (x,y,z),
[] => return Err("empty"),
_ => return Err("wrong length"),
};
Ok(v)
When passing "" as s, I would expect to receive an Err("empty"), but I receive Err("wrong length") instead. What am I doing wrong?
s.split(sep) will always return at least one element: if the separator sep doesn't appear in the input string, it will just yield a single element that is the entire string. More generally, if the separator sep appears N times in the string s, the iterator will always yield N+1 elements.
So when you call "".split(','), you will get an iterator of a single element "" because the separator appears 0 times.
Here are two different approaches you can use if you want to avoid this:
// filter any empty segments, for example:
// "" => [] instead of [""]
// "x,y" => ["x", "y"]
// "x,,y" => ["x", "y"] instead of ["x", "", "y"]
let v: Vec<_> = s.split(',').filter(|item| !item.is_empty()).collect();
// return empty array if input is empty, otherwise return all segments (even if empty):
// "" => [] instead of [""]
// "x,y" => ["x", "y"]
// "x,,y" => ["x", "", "y"]
let v: Vec<_> = if s.is_empty() {
vec![]
} else {
s.split(',').collect()
};
Related
So basically, I want to do this
for i=0;i<x.len()-1
for j=i;j<x.len
//do stuff with x[j] and x[i] at the same time
For example, I want to use the same tehnique as sorting using 2 for loops, comparing element with element and interchange them. I do not want to sort however, just gave this example for better understanding.
Can i somehow do like:
for x in vec.iter()
for y in x.next()
or something like this?
Also, can I somehow remember the position of a certain element while iterating?
Thank you!
You could use ranges
for i in 0..vec.len()-1 {
for j in i..vec.len() {
// do something with vec[i] and vec[j]
}
}
Your suggested code:
for x in vec.iter()
for y in x.next()
wouldn't work even if it were syntactically valid because x is not an iterator, it's an element of vec. To get at the iterator, you need to store it in a variable and desugar the for loop into while let:
let mut iter = v.iter();
while let Some(x) = iter {
// ...
}
Now that we have an explicit iter, we can make the inner loop iterate over the remaining items. We can't just iterate over iter because that would exhaust it for the outer loop, but we can clone it to obtain a copy of the outer iterator at its current position:
let mut iter = v.iter();
while let Some(x) = iter {
for y in iter.clone() {
// ...
}
}
Note that we don't need to explicitly call iter.next() before the inner loop, as the first item will have already been spent by the outer loop, and the inner loop will naturally observe only the remaining items of each iteration.
Complete code would look like this:
let v = vec![1, 2, 3];
let mut iter = v.iter();
while let Some(x) = iter.next() {
println!("x = {}", x);
for y in iter.clone() {
println!(" y = {}", y);
}
}
Output:
x = 1
y = 2
y = 3
x = 2
y = 3
x = 3
I want to change the value of a specific attribute of a vector of objects.
my code and logic are as follows:
let mut person_found: Vec<Person> = persons.clone().into_iter().filter(|x| x.id == "3")
.map(|x| x.name = "Carlos".to_string()).collect();
println!("original: {:?}", &persons);
println!("modified person: {:?}", &person_found);
But it gives me the following error and I can't understand it well.
error[E0277]: a value of type `Vec<Person>` cannot be built from an iterator over elements of type `()`
--> src\main.rs:17:45
|
17 | .map(|x| x.name = "Carlos".to_string()).collect();
| ^^^^^^^ value of type `Vec<Person>` cannot be built from `std::iter::Iterator<Item=()>`
|
= help: the trait `FromIterator<()>` is not implemented for `Vec<Person>`
The result of an assignment is () (the unit value). So if you do y = (x = 123); then x is assigned 123 and y is assigned ().
The Rust Reference has a short sentence stating:
An assignment expression always produces the unit value.
β Assignment expressions - Operator expressions - The Rust Reference
You need to change so it explicitly returns x on the next line, for that to work. If you want to mutate x then you also need to change |x| to |mut x|.
let person_found: Vec<Person> = persons
.clone()
.into_iter()
.filter(|x| x.id == "3")
.map(|mut x| {
x.name = "Carlos".to_string();
x
})
.collect();
Alternatively, instead of cloning the whole persons Vec upfront, I'd instead use cloned() after filter() or clone() in map(), i.e. only when needed.
let person_found: Vec<Person> = persons
.iter()
.filter(|x| x.id == "3")
.cloned()
.map(|mut x| {
x.name = "Carlos".to_string();
x
})
.collect();
You are returning nothing in your map expr line. You can handle with filter_map though:
let mut person_found: Vec<Person> = persons
.iter()
.filter_map(|p|
(p.id == 3).then_some(Person {
name: String::from("Carlos"),
..p.clone()}))
.collect();
Playground
How to break a vector such as [9,7,6,3,4,0,1,7,3,9] -> [[9,7,6,3],[4,1],[7,3],[9]] -> [25,5,10,9]?
The logic behind it is that a vector is broken into subvectors where each subsequent element is smaller than previous one(0'z are ignored), a descending sequence . When the subvectors are formed, each one is replaced with a sum of all of its elements.
[https://www.codewars.com/kata/5f8fb3c06c8f520032c1e091][1]
Iterate over the elements of the nums to build up the split. For every number, compare it to the last number to decide whether to create a sublist, or append to the existing one:
let nums = vec![9,7,6,3,4,0,1,7,3,9];
let mut split: Vec<Vec<i32>> = vec![vec![]];
for num in nums.iter().filter(|n| **n != 0) {
let sublist = split.last_mut().unwrap();
match sublist.last_mut() {
Some(x) if num > x => {
split.push(vec![*num]);
}
_ => sublist.push(*num),
}
}
let split = split; // make split immmutable
let summed: Vec<i32> = split.iter().map(|v| v.iter().sum()).collect();
(try in playground)
It's probably possible to make a more elegant solution using Iterator::partition_in_place, but that fn is sadly unstable for now.
I want to read a whitespace-separated file (could be tab or uneven space) into tuples:
use std::io::{BufReader, BufRead, Cursor};
fn main() {
let data = "
A 1 Pass
B 2 Fail
C 3 Fail
";
let lines = BufReader::new(Cursor::new(data))
.lines();
for line in lines {
let line_temp = line.unwrap();
let broken_line: Vec<&str> = line_temp.split(" ").collect(); // This works
// I want something like below:
// let (a, b, c) = ("A", 1, "Pass");
println!("{:?}", broken_line);
}
}
I want a to store the first column, b to store second column and so on.
a = A, b = 1, c = Pass
a = B, b = 2, c = Fail
// ...
Assuming your data is well-formed (aside from empty lines) and you don't have to worry about validating each individual line then you can do this
fn main() {
let data = "
A 1 Pass
B 2 Fail
C 3 Fail
";
for line in data.lines() {
let line = line.trim();
if line.is_empty() {
continue;
}
let mut parts = line.split_whitespace();
let tuple = (
parts.next().unwrap(),
parts.next().unwrap().parse::<i32>().unwrap(),
parts.next().unwrap(),
);
println!("{:?}", tuple);
}
}
playground
From the documentation, it's not clear. In Java you could use the split method like so:
"some string 123 ffd".split("123");
Use split()
let mut split = "some string 123 ffd".split("123");
This gives an iterator, which you can loop over, or collect() into a vector.
for s in split {
println!("{}", s)
}
let vec = split.collect::<Vec<&str>>();
// OR
let vec: Vec<&str> = split.collect();
There are three simple ways:
By separator:
s.split("separator") | s.split('/') | s.split(char::is_numeric)
By whitespace:
s.split_whitespace()
By newlines:
s.lines()
By regex: (using regex crate)
Regex::new(r"\s").unwrap().split("one two three")
The result of each kind is an iterator:
let text = "foo\r\nbar\n\nbaz\n";
let mut lines = text.lines();
assert_eq!(Some("foo"), lines.next());
assert_eq!(Some("bar"), lines.next());
assert_eq!(Some(""), lines.next());
assert_eq!(Some("baz"), lines.next());
assert_eq!(None, lines.next());
There is a special method split for struct String:
fn split<'a, P>(&'a self, pat: P) -> Split<'a, P> where P: Pattern<'a>
Split by char:
let v: Vec<&str> = "Mary had a little lamb".split(' ').collect();
assert_eq!(v, ["Mary", "had", "a", "little", "lamb"]);
Split by string:
let v: Vec<&str> = "lion::tiger::leopard".split("::").collect();
assert_eq!(v, ["lion", "tiger", "leopard"]);
Split by closure:
let v: Vec<&str> = "abc1def2ghi".split(|c: char| c.is_numeric()).collect();
assert_eq!(v, ["abc", "def", "ghi"]);
split returns an Iterator, which you can convert into a Vec using collect: split_line.collect::<Vec<_>>(). Going through an iterator instead of returning a Vec directly has several advantages:
split is lazy. This means that it won't really split the line until you need it. That way it won't waste time splitting the whole string if you only need the first few values: split_line.take(2).collect::<Vec<_>>(), or even if you need only the first value that can be converted to an integer: split_line.filter_map(|x| x.parse::<i32>().ok()).next(). This last example won't waste time attempting to process the "23.0" but will stop processing immediately once it finds the "1".
split makes no assumption on the way you want to store the result. You can use a Vec, but you can also use anything that implements FromIterator<&str>, for example a LinkedList or a VecDeque, or any custom type that implements FromIterator<&str>.
There's also split_whitespace()
fn main() {
let words: Vec<&str> = " foo bar\t\nbaz ".split_whitespace().collect();
println!("{:?}", words);
// ["foo", "bar", "baz"]
}
The OP's question was how to split with a multi-character string and here is a way to get the results of part1 and part2 as Strings instead in a vector.
Here splitted with the non-ASCII character string "ββπ€" in place of "123":
let s = "ββπ€"; // also works with non-ASCII characters
let mut part1 = "some string ββπ€ ffd".to_string();
let _t;
let part2;
if let Some(idx) = part1.find(s) {
part2 = part1.split_off(idx + s.len());
_t = part1.split_off(idx);
}
else {
part2 = "".to_string();
}
gets: part1 = "some string "
Β Β Β Β Β part2 = " ffd"
If "ββπ€" not is found part1 contains the untouched original String and part2 is empty.
Here is a nice example in Rosetta Code -
Split a character string based on change of character - of how you can turn a short solution using split_off:
fn main() {
let mut part1 = "gHHH5YY++///\\".to_string();
if let Some(mut last) = part1.chars().next() {
let mut pos = 0;
while let Some(c) = part1.chars().find(|&c| {if c != last {true} else {pos += c.len_utf8(); false}}) {
let part2 = part1.split_off(pos);
print!("{}, ", part1);
part1 = part2;
last = c;
pos = 0;
}
}
println!("{}", part1);
}
into that
Task
Split a (character) string into comma (plus a blank) delimited strings based on a change of character (left to right).
If you are looking for the Python-flavoured split where you tuple-unpack the two ends of the split string, you can do
if let Some((a, b)) = line.split_once(' ') {
// ...
}