How to convert iter.next() to an array index? - arrays

I would implement an array v to mark the current vertex as visited. However, iter.next() cannot be an index of LinkedList or an index of an array.
The output of iter.next() is not an integer, it is an option Some.
How to use the value in Some? If I would use value in Some as an index of array. Should I convert the value into an integer?
fn bfs(s: usize) {
let mut cells = vec![LinkedList::<usize>::new(); 4];
for (i, cell) in cells.iter_mut().enumerate() {
cell.push_front(i);
}
cells[0].push_back(1);
cells[0].push_back(2);
cells[1].push_back(2);
cells[2].push_back(0);
cells[2].push_back(3);
cells[3].push_back(3);
let mut iter = cells[s].iter();
let mut v = vec![0; 4];
// Entry point
for i in 0..cells[s].len() {
// It is incorrect, "iter.next()" it cannot be a index of LinkedList
// vector "v" to keep track of which vertex has been traversed
//v[iter.next()] = 1;
}
}
fn main() {
bfs(2);
}

iter.next() return an Option, you just need to deconstruct it:
for i in 0..cells[s].len() {
let x = match iter.next() {
Some(x) => *x,
None => return,
};
v[x] = 1;
}

Rust's for loops can iterate for you, so you don't need to explicitly create an iterator or call next(). The Option will already be unwrapped inside the loop, because the loop automatically ends when next() returns None.
for &i in &cells[s] {
v[i] = 1;
}
This saves you having to keep track of index variables just to create a loop. And the code is a lot shorter and more readable.

This is the final version
fn bfs(s: usize) {
let mut cells = vec![LinkedList::<usize>::new(); 4];
let mut traversal = LinkedList::new();
let mut queue = LinkedList::new();
for (i, cell) in cells.iter_mut().enumerate() {
cell.push_front(i);
}
cells[0].push_back(1);
cells[0].push_back(2);
cells[1].push_back(2);
cells[2].push_back(0);
cells[2].push_back(3);
cells[3].push_back(3);
let mut v = vec![0; 4];
let mut done = false;
let c = s;
while !done {
for &x in &cells[c] {
if v[x] == 0 {
v[x] = 1;
queue.push_back(x);
traversal.push_back(x)
}
}
let c = queue.pop_back();
if queue.is_empty() {
// walkthrough
for i in 0..cells.len() {
if v[i] == 0 {
v[i] = 1;
queue.push_back(i);
traversal.push_back(i)
}
}
if queue.is_empty() {
done = true;
}
}
}
println!("{:?}", traversal);
}

You can use the reference of the integer value in Some as the index of the array.
let mut v = vec![0; 4];
while !done {
let mut iter = cells[c].iter().peekable();
// Entry point
while let Some(&x) = iter.next_if(|&&n| v[n] == 0) {
v[x] = 1;
queue.push_back(x);
traversal.push_back(x)
}
…

Related

Syntax for updating underlying instance when looping?

I have a HashMap with different hashing algorithms which implement digest::DynDigest, but when they are looped over and update()d the final hash is always the same (Sha224 d14a...) (you can change the file PathBuf parameter to anything and get the exact same hash every time). So the update() and/or finalize() is not updating the instance in the HashMap.
What is the correct way to loop over the different hashers so that HashMap keeps correct updated state?
use digest::{Digest, DynDigest}; // https://crates.io/crates/digest/reverse_dependencies
use sha2; // implements digest::DynDigest
use std::collections::HashMap;
use std::fs::File;
use std::io;
use std::io::{BufReader, Read};
use std::path::PathBuf;
fn checksum_to_hex(bytes: &[u8]) -> String {
let mut s: String = String::new();
for b in bytes {
s.push_str(format!("{:02x}", b).as_str());
}
s
}
pub fn hash_file(
fpath: PathBuf,
hashers: HashMap<&str, Box<dyn DynDigest>>,
) -> io::Result<HashMap<&str, String>> {
let f = File::open(fpath).expect("failed to open file for hashing");
let mut buffer = [0u8; 1048576];
let mut reader = BufReader::new(f);
loop {
let count = reader.read(&mut buffer)?;
if count == 0 {
break;
}
for (_, mut hasher) in hashers.to_owned() {
hasher.update(&buffer[..count]);
}
}
// Hash results per hasher
let mut hashes: HashMap<&str, String> = HashMap::new();
for (k, hasher) in hashers.to_owned() {
let res = checksum_to_hex(hasher.finalize().to_vec().as_slice());
hashes.insert(k, res);
}
Ok(hashes)
}
fn main() {
let mut hashers: HashMap<&str, Box<dyn DynDigest>> = HashMap::new();
hashers.insert("Sha224", Box::new(sha2::Sha224::new()));
hashers.insert("Sha256", Box::new(sha2::Sha256::new()));
hashers.insert("Sha512", Box::new(sha2::Sha512::new()));
for (htype, hashres) in hash_file(PathBuf::from("/bin/ls"), hashers).expect("") {
println!(" {} {}", htype, hashres);
}
}
Rust Playground
You need to replace the first hashers.to_owned() with hashers.iter_mut():
pub fn hash_file(
fpath: PathBuf,
mut hashers: HashMap<&str, Box<dyn DynDigest>>,
) -> io::Result<HashMap<&str, String>> {
let f = File::open(fpath).expect("failed to open file for hashing");
let mut buffer = [0u8; 1048576];
let mut reader = BufReader::new(f);
loop {
let count = reader.read(&mut buffer)?;
if count == 0 {
break;
}
for (_, hasher) in hashers.iter_mut() {
// or `for hasher in hashers.values_mut()`
hasher.update(&buffer[..count]);
}
}
// Hash results per hasher
let mut hashes: HashMap<&str, String> = HashMap::new();
for (k, hasher) in hashers {
let res = checksum_to_hex(hasher.finalize().to_vec().as_slice());
hashes.insert(k, res);
}
Ok(hashes)
}
playground
.to_owned() will clone (create an independent deep copy) the map. When you iterate over that, you're iterating through a different map than the one you eventually return.
Instead, you want to iterate over references to the elements: this is what .iter_mut() (or .values_mut() if you only need the values) will give you.
This way, you don't need the second .to_owned(), but you do need to mark the hashers argument as mutable.

Parallel array elements assignment causes crash in Swift

I want set elements of array concurrently,so I write code to test:
let Count = 1000
let SubRangeCount = 100
var ary = [Int](repeating: 0, count: Count)
let times = Count/SubRangeCount
let _ = DispatchQueue.global(qos: .userInitiated)
DispatchQueue.concurrentPerform(iterations: times){ idx in
for i in (idx * SubRangeCount)..<((idx+1) * SubRangeCount) {
ary[i] = Int.random(in: 0...Int.max)
}
}
}
above code working great!
But when I change like this:
let Count = 10000
let SubRangeCount = 1000
It's crash!
In playground it complain "Fatal error: UnsafeMutablePointer.initialize overlapping range: file Swift/UnsafePointer.swift, line 832"
But you can see every range of array's assignments is NOT overlapping!
So how to fix it? Thanks!
Array is not thread safe on Swift so you need to synchronise access to it, for instance with objc_sync_enter and objc_sync_exit:
func synchronized<T : AnyObject, U>(_ obj: T, closure: () -> U) -> U {
objc_sync_enter(obj)
defer {
objc_sync_exit(obj)
}
return closure()
}
var arr = [Int](repeating: 0, count: 100_000)
let obj = arr as NSObject // Object to synchronise
DispatchQueue.concurrentPerform(iterations: 100_000){ index in
synchronized(obj) {
arr[index] = Int.random(in: 0...Int.max)
}
}
Here the array is concurrently mutated from multiple threads, which creates a race condition. To avoid this you have to perform the critical operation in a thread-safe way. You can take help from Grand Central Dispatch in the Apple platform - which lets us deal with threads using its much simpler queue-based abstractions.
let Count = 10000
let SubRangeCount = 1000
var ary = [Int](repeating: 0, count: Count)
let times = Count/SubRangeCount
let serialQueue = DispatchQueue(label: "serial.Queue")
DispatchQueue.concurrentPerform(iterations: times){ idx in
print(idx)
for i in (idx * SubRangeCount)..<((idx+1) * SubRangeCount) {
serialQueue.async {
ary[i] = Int.random(in: 0...Int.max)
}
}
}
You can also use lock.
let Count = 10000
let SubRangeCount = 1000
var ary = [Int](repeating: 0, count: Count)
let lock = NSLock()
let times = Count/SubRangeCount
DispatchQueue.concurrentPerform(iterations: times){ idx in
print(idx)
for i in (idx * SubRangeCount)..<((idx+1) * SubRangeCount) {
lock.lock()
ary[i] = Int.random(in: 0...Int.max)
lock.unlock()
}
}
I found a workaround for it:
DispatchQueue.concurrentPerform(iterations: enumTimes){ idx in
var subAry = ary1[(idx * SubRangeCount)..<((idx+1) * SubRangeCount)]
for i in subAry.indices {
subAry[i] = Int.random(in: 0...Int.max)
}
}
It's no Crash, and fast 3x-10x than serialize assignments.

Does Swift offer any built-in function to return the result of appending to an immutable array?

Writing the question and answer from here, I'm curious to know if there is any simpler way to write the following:
var nums = [1,2,3]
let sum1 = nums.reduce([Int]()){
let temp = $0
temp.append($1)
return temp
}
I know I can do:
var nums = [1,2,3]
let sum1 = nums.reduce([Int]()){
return $0 + [$1]
}
But that comes off as a hack.
To explain this better, I want to get closer to the example (from docs) below, just that it should be for an array:
let numbers = [1, 2, 3, 4]
let numberSum = numbers.reduce(0, { x, y in
x + y
})
EDIT:
Since folks asked what was I trying to achieve:
I was doing the leetcode's group Anagram's challenge.
My solution was:
struct WordTraits: Equatable{
let count: Int
let charactersSet: Set<Character>
}
struct Word: Equatable{
let string: String
let wordTraits: WordTraits
}
class Solution{
func groupAnagrams(_ strs: [String]) -> [[String]]{
var words : [Word] = []
var answers : [(traits: WordTraits, words: [Word])] = []
var count = 0
strs.forEach{ str in
count += 1
let count = str.count
let string = str
let characterSet = Set(str)
let wordTraits = WordTraits(count: count, charactersSet: characterSet)
let word = Word(string: string, wordTraits: wordTraits)
words.append(word)
}
while words.count != 0{
let word = words[0]
let traits = word.wordTraits
var isWordAdded = false
for (i, answer) in answers.enumerated(){
if answer.traits == traits{
answers[i].words.append(word)
isWordAdded = true
break
}
}
if !isWordAdded{
answers.append((traits: traits, words:[word]))
}
words.removeFirst()
}
let emptyArray : [[String]] = []
let finalAnswer = answers.reduce(emptyArray, { total, answer in
let strings : [String] = answer.words.reduce([String](), {
return $0 + [$1.string]
})
return total + [strings]
})
return finalAnswer
}
}
let s = Solution()
print(s.groupAnagrams(["ate", "eta", "beta", "abet"])) // [["ate", "eta"], ["beta", "abet"]]
reduce(..) has to know which type it is working with. To infer this it can use the return type or the type of the first argument. So you can also write:
var nums = [1,2,3]
let sum1: [Int] = nums.reduce([]){
return $0 + [$1]
}
[$1] can't be replaced with $1 because +-operator between value and collection is undefined.
Nope. But you can add it:
extension Array {
func appending(_ newElement: Element) -> Array<Element> {
return self + [newElement]
}
func appending(contentsOf sequence: Sequence) -> Array<Element> {
return self + sequence
}
}
Um, how about the + operator?
let nums = [1, 3, 5]
let more = nums + [7]
Your code is trying to convert a complex structure to an array of arrays. You can use map for this.
This should work:
let finalAnswer = answers.map { answer in
answer.words.map {
$0.string
}
}
Edit:
I was able to solve it using minimal code:
class Solution {
func groupAnagrams(_ words: [String]) -> [[String]] {
let processedWords = words.map {
(key: String($0.sorted()), value: $0)
}
return Dictionary(grouping: processedWords, by: { $0.key }).map { groupedValue in
groupedValue.value.map {
$0.value
}
}
}
}
You've greatly overcomplicated your computation of "final answers". It could just be:
return answers.map { $0.words.map { $0.string } }

How to mutate an array of integers in-place in swift through filtering

One can filter an array like this in swift:
var numbers = Array(1...1000000)
numbers = numbers.filter( { return $0 % 2 == 0 } )
Is it possible to filter and avoid the copy operation, that occurs when the filtering is done, e.g mutating the original array.
In a similar way to this pseudocode:
numbers.MutablefilterOperation({ return $0 % 2 == 0})
In C++ the equvivalent to what is going on in Swift above would be:
std::vector<int> originalNumbers(1000000);
std::vector<int> newNumbers;
std::copy_if (originalNumbers.begin(), originalNumbers.end(), std::back_inserter(newNumbers), [](int i) { return i % 2 == 0 } );
What I would like to achieve for performance reasons:
std::vector<int> originalNumbers(1000000);
auto pos = std::remove_if(originalNumbers.begin(), originalNumbers.end(), [](int x) { return x % 2 == 0; });
originalNumbers.erase(pos, originalNumbers.end());
This implementation should do the filtering without having to make a temporary copy of the entire array in the process (unless a copy of it is referenced by another variable, see "Copy on Write")
extension Array {
mutating func filterInPlace(isIncluded: (Element) throws -> Bool) rethrows {
var writeIndex = self.startIndex
for readIndex in self.indices {
let element = self[readIndex]
let include = try isIncluded(element)
if include {
if writeIndex != readIndex {
self[writeIndex] = element
}
writeIndex = self.index(after: writeIndex)
}
}
self.removeLast(self.distance(from: writeIndex, to: self.endIndex))
}
}
// example:
var arr = [6,2,6,5,2,5,6,2,2,1,6,7,3]
arr.filterInPlace { $0 % 2 == 1 }
print(arr) // [5, 5, 1, 7, 3]

Iterate over two arrays simultaneously

I am new to Swift. I have been doing Java programming. I have a scenario to code for in Swift.
The following code is in Java. I need to code in Swift for the following scenario
// With String array - strArr1
String strArr1[] = {"Some1","Some2"}
String strArr2[] = {"Somethingelse1","Somethingelse2"}
for( int i=0;i< strArr1.length;i++){
System.out.println(strArr1[i] + " - "+ strArr2[i]);
}
I have a couple of arrays in swift
var strArr1: [String] = ["Some1","Some2"]
var strArr2: [String] = ["Somethingelse1","Somethingelse2"]
for data in strArr1{
println(data)
}
for data in strArr2{
println(data)
}
// I need to loop over in single for loop based on index.
Could you please provide your help on the syntaxes for looping over based on index
You can use zip(), which creates
a sequence of pairs from the two given sequences:
let strArr1 = ["Some1", "Some2"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
for (e1, e2) in zip(strArr1, strArr2) {
print("\(e1) - \(e2)")
}
The sequence enumerates only the "common elements" of the given sequences/arrays. If they have different length then the additional
elements of the longer array/sequence are simply ignored.
With Swift 5, you can use one of the 4 following Playground codes in order to solve your problem.
#1. Using zip(_:_:) function
In the simplest case, you can use zip(_:_:) to create a new sequence of pairs (tuple) of the elements of your initial arrays.
let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
let sequence = zip(strArr1, strArr2)
for (el1, el2) in sequence {
print("\(el1) - \(el2)")
}
/*
prints:
Some1 - Somethingelse1
Some2 - Somethingelse2
*/
#2. Using Array's makeIterator() method and a while loop
It is also easy to loop over two arrays simultaneously with a simple while loop and iterators:
let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
var iter1 = strArr1.makeIterator()
var iter2 = strArr2.makeIterator()
while let el1 = iter1.next(), let el2 = iter2.next() {
print("\(el1) - \(el2)")
}
/*
prints:
Some1 - Somethingelse1
Some2 - Somethingelse2
*/
#3. Using a custom type that conforms to IteratorProtocol
In some circumstances, you may want to create you own type that pairs the elements of your initials arrays. This is possible by making your type conform to IteratorProtocol. Note that by making your type also conform to Sequence protocol, you can use instances of it directly in a for loop:
struct TupleIterator: Sequence, IteratorProtocol {
private var firstIterator: IndexingIterator<[String]>
private var secondIterator: IndexingIterator<[String]>
init(firstArray: [String], secondArray: [String]) {
self.firstIterator = firstArray.makeIterator()
self.secondIterator = secondArray.makeIterator()
}
mutating func next() -> (String, String)? {
guard let el1 = firstIterator.next(), let el2 = secondIterator.next() else { return nil }
return (el1, el2)
}
}
let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
let tupleSequence = TupleIterator(firstArray: strArr1, secondArray: strArr2)
for (el1, el2) in tupleSequence {
print("\(el1) - \(el2)")
}
/*
prints:
Some1 - Somethingelse1
Some2 - Somethingelse2
*/
#4. Using AnyIterator
As an alternative to the previous example, you can use AnyIterator. The following code shows a possible implementation of it inside an Array extension method:
extension Array {
func pairWithElements(of array: Array) -> AnyIterator<(Element, Element)> {
var iter1 = self.makeIterator()
var iter2 = array.makeIterator()
return AnyIterator({
guard let el1 = iter1.next(), let el2 = iter2.next() else { return nil }
return (el1, el2)
})
}
}
let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
let iterator = strArr1.pairWithElements(of: strArr2)
for (el1, el2) in iterator {
print("\(el1) - \(el2)")
}
/*
prints:
Some1 - Somethingelse1
Some2 - Somethingelse2
*/
Try This:
zip([0,2,4,6], [1,3,5,7]).forEach {
print($0,$1)
}
zip([0,2,4,6], [1,3,5,7]).forEach {
print($0.0,$0.1)
}
You could also enumerate over one array and used the index to look inside the second array:
Swift 1.2:
for (index, element) in enumerate(strArr1) {
println(element)
println(strArr2[index])
}
Swift 2:
for (index, element) in strArr1.enumerate() {
print(element)
print(strArr2[index])
}
Swift 3:
for (index, element) in strArr1.enumerated() {
print(element)
print(strArr2[index])
}
You could use Range if you still want to use for in.
var strArr1: [String] = ["Some1","Some2"]
var strArr2: [String] = ["Somethingelse1","Somethingelse2"]
for i in Range(start: 0, end: strArr1.count) {
println(strArr1[i] + " - " + strArr2[i])
}
for(var i = 0; i < strArr1.count ; i++)
{
println(strArr1[i] + strArr2[i])
}
That should do it. Never used swift before so make sure to test.
Updated to recent Swift syntax
for i in 0..< strArr1.count {
print(strArr1[i] + strArr2[i])
}
> Incase of unequal count
let array1 = ["some1","some2"]
let array2 = ["some1","some2","some3"]
var iterated = array1.makeIterator()
let finalArray = array2.map({
let itemValue = iterated.next()
return "\($0)\(itemValue != nil ? "-"+itemValue! : EmptyString)" })
// result : ["some1-some1","some2-some2","some3"]

Resources