├── .gitignore ├── .travis.yml ├── Cargo.toml ├── MIT-LICENSE ├── README.md ├── alternates.txt ├── appveyor.yml ├── benches └── bench.rs ├── src ├── iter.rs ├── lib.rs ├── sorted_vec.rs ├── string.rs └── vec.rs └── tests ├── lib.rs ├── sorted_vec.rs ├── string.rs └── vec.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | doc/ 4 | **/*.swp 5 | *.vim 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | os: 3 | - linux 4 | - osx 5 | rust: 6 | - stable 7 | - beta 8 | - nightly 9 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "array_tool" 3 | version = "1.0.4" 4 | authors = ["Daniel P. Clark <6ftdan@gmail.com>"] 5 | description = "Helper methods for processing collections" 6 | documentation = "http://danielpclark.github.io/array_tool/index.html" 7 | homepage = "https://github.com/danielpclark/array_tool" 8 | repository = "https://github.com/danielpclark/array_tool" 9 | license = "MIT OR Apache-2.0" 10 | readme = "README.md" 11 | keywords = ["vector","string","grapheme","unique","substitution"] 12 | categories = ["text-processing", "data-structures", "value-formatting"] 13 | 14 | [badges] 15 | travis-ci = { repository = "danielpclark/array_tool", branch = "master" } 16 | appveyor = { repository = "danielpclark/array_tool", branch = "master", service = "github" } 17 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015-2018 Daniel P. Clark 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # array_tool 2 | [![Build Status](https://travis-ci.org/danielpclark/array_tool.svg?branch=master)](https://travis-ci.org/danielpclark/array_tool) 3 | [![Build Status](https://ci.appveyor.com/api/projects/status/dffq3dwb8w220q4f/branch/master?svg=true)](https://ci.appveyor.com/project/danielpclark/array-tool/branch/master) 4 | [![Documentation](https://img.shields.io/badge/docs-100%25-brightgreen.svg)](http://danielpclark.github.io/array_tool/index.html) 5 | [![crates.io version](https://img.shields.io/crates/v/array_tool.svg)](https://crates.io/crates/array_tool) 6 | [![License](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)]() 7 | 8 | Array helpers for Rust. Some of the most common methods you would 9 | use on Arrays made available on Vectors. Polymorphic implementations 10 | for handling most of your use cases. 11 | 12 | 13 | ### Installation 14 | 15 | Add the following to your Cargo.toml file 16 | ```toml 17 | [dependencies] 18 | array_tool = "~1.0.3" 19 | ``` 20 | 21 | And in your rust files where you plan to use it put this at the top 22 | ```rust 23 | extern crate array_tool; 24 | ``` 25 | 26 | And if you plan to use all of the Vector helper methods available you may do 27 | ```rust 28 | use array_tool::vec::*; 29 | ``` 30 | 31 | This crate has helpful methods for strings as well. 32 | 33 | ## Iterator Usage 34 | 35 | ```rust 36 | use array_tool::iter::ZipOpt; 37 | fn zip_option(self, other: U) -> ZipOption 38 | where Self: Sized, U: IntoIterator; 39 | // let a = vec![1]; 40 | // let b = vec![]; 41 | // a.zip_option(b).next() // input 42 | // Some((Some(1), None)) // return value 43 | ``` 44 | 45 | ## Vector Usage 46 | 47 | ```rust 48 | pub fn uniques(a: Vec, b: Vec) -> Vec> 49 | // array_tool::uniques(vec![1,2,3,4,5], vec![2,5,6,7,8]) // input 50 | // vec![vec![1,3,4], vec![6,7,8]] // return value 51 | 52 | use array_tool::vec::Uniq; 53 | fn uniq(&self, other: Vec) -> Vec; 54 | // vec![1,2,3,4,5,6].uniq( vec![1,2,5,7,9] ) // input 55 | // vec![3,4,6] // return value 56 | fn uniq_via bool>(&self, other: Self, f: F) -> Self; 57 | // vec![1,2,3,4,5,6].uniq_via( vec![1,2,5,7,9], |&l, r| l == r + 2 ) // input 58 | // vec![1,2,4,6] // return value 59 | fn unique(&self) -> Vec; 60 | // vec![1,2,1,3,2,3,4,5,6].unique() // input 61 | // vec![1,2,3,4,5,6] // return value 62 | fn unique_via bool>(&self, f: F) -> Self; 63 | // vec![1.0,2.0,1.4,3.3,2.1,3.5,4.6,5.2,6.2]. 64 | // unique_via( |l: &f64, r: &f64| l.floor() == r.floor() ) // input 65 | // vec![1.0,2.0,3.3,4.6,5.2,6.2] // return value 66 | fn is_unique(&self) -> bool; 67 | // vec![1,2,1,3,4,3,4,5,6].is_unique() // input 68 | // false // return value 69 | // vec![1,2,3,4,5,6].is_unique() // input 70 | // true // return value 71 | 72 | use array_tool::vec::Shift; 73 | fn unshift(&mut self, other: T); // no return value, modifies &mut self directly 74 | // let mut x = vec![1,2,3]; 75 | // x.unshift(0); 76 | // assert_eq!(x, vec![0,1,2,3]); 77 | fn shift(&mut self) -> Option; 78 | // let mut x = vec![0,1,2,3]; 79 | // assert_eq!(x.shift(), Some(0)); 80 | // assert_eq!(x, vec![1,2,3]); 81 | 82 | use array_tool::vec::Intersect; 83 | fn intersect(&self, other: Vec) -> Vec; 84 | // vec![1,1,3,5].intersect(vec![1,2,3]) // input 85 | // vec![1,3] // return value 86 | fn intersect_if bool>(&self, other: Vec, validator: F) -> Vec; 87 | // vec!['a','a','c','e'].intersect_if(vec!['A','B','C'], |l, r| l.eq_ignore_ascii_case(r)) // input 88 | // vec!['a','c'] // return value 89 | 90 | use array_tool::vec::Join; 91 | fn join(&self, joiner: &'static str) -> String; 92 | // vec![1,2,3].join(",") // input 93 | // "1,2,3" // return value 94 | 95 | use array_tool::vec::Times; 96 | fn times(&self, qty: i32) -> Vec; 97 | // vec![1,2,3].times(3) // input 98 | // vec![1,2,3,1,2,3,1,2,3] // return value 99 | 100 | use array_tool::vec::Union; 101 | fn union(&self, other: Vec) -> Vec; 102 | // vec!["a","b","c"].union(vec!["c","d","a"]) // input 103 | // vec![ "a", "b", "c", "d" ] // return value 104 | ``` 105 | 106 | ## String Usage 107 | 108 | ```rust 109 | use array_tool::string::ToGraphemeBytesIter; 110 | fn grapheme_bytes_iter(&'a self) -> GraphemeBytesIter<'a>; 111 | // let string = "a s—d féZ"; 112 | // let mut graphemes = string.grapheme_bytes_iter() 113 | // graphemes.skip(3).next(); // input 114 | // [226, 128, 148] // return value for emdash `—` 115 | 116 | use array_tool::string::Squeeze; 117 | fn squeeze(&self, targets: &'static str) -> String; 118 | // "yellow moon".squeeze("") // input 119 | // "yelow mon" // return value 120 | // " now is the".squeeze(" ") // input 121 | // " now is the" // return value 122 | 123 | use array_tool::string::Justify; 124 | fn justify_line(&self, width: usize) -> String; 125 | // "asd as df asd".justify_line(16) // input 126 | // "asd as df asd" // return value 127 | // "asd as df asd".justify_line(18) // input 128 | // "asd as df asd" // return value 129 | 130 | use array_tool::string::SubstMarks; 131 | fn subst_marks(&self, marks: Vec, chr: &'static str) -> String; 132 | // "asdf asdf asdf".subst_marks(vec![0,5,8], "Z") // input 133 | // "Zsdf ZsdZ asdf" // return value 134 | 135 | use array_tool::string::WordWrap; 136 | fn word_wrap(&self, width: usize) -> String; 137 | // "01234 67 9 BC EFG IJ".word_wrap(6) // input 138 | // "01234\n67 9\nBC\nEFG IJ" // return value 139 | 140 | use array_tool::string::AfterWhitespace; 141 | fn seek_end_of_whitespace(&self, offset: usize) -> Option; 142 | // "asdf asdf asdf".seek_end_of_whitespace(6) // input 143 | // Some(9) // return value 144 | // "asdf".seek_end_of_whitespace(3) // input 145 | // Some(0) // return value 146 | // "asdf ".seek_end_of_whitespace(6) // input 147 | // None // return_value 148 | 149 | ``` 150 | 151 | ## Future plans 152 | 153 | Expect methods to become more polymorphic over time (same method implemented 154 | for similar & compatible types). I plan to implement many of the methods 155 | available for Arrays in higher languages; such as Ruby. Expect regular updates. 156 | 157 | ## License 158 | 159 | Licensed under either of 160 | 161 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 162 | * MIT license ([MIT-LICENSE](MIT-LICENSE) or http://opensource.org/licenses/MIT) 163 | 164 | at your option. 165 | 166 | ### Contribution 167 | 168 | Unless you explicitly state otherwise, any contribution intentionally submitted 169 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any 170 | additional terms or conditions. 171 | -------------------------------------------------------------------------------- /alternates.txt: -------------------------------------------------------------------------------- 1 | 43% SLOWER implementation 2 | 3 | pub fn experimental_uniques + std::clone::Clone>(a: Vec, b: Vec) -> Vec> { 4 | let mut first_uniq = a.clone(); 5 | let mut second_uniq = b.clone(); 6 | 7 | let mut x = first_uniq.len(); 8 | 9 | 'outer: loop { 10 | x -= 1; 11 | let mut y = second_uniq.len(); 12 | let mut removed = false; 13 | 'inner: loop { 14 | y -= 1; 15 | if first_uniq[x] == second_uniq[y] { 16 | first_uniq.remove(x); 17 | second_uniq.remove(y); 18 | removed = true 19 | } 20 | if x == 0{ 21 | if y == 0{ 22 | break 'outer 23 | } 24 | } 25 | else { 26 | if y == 0{ 27 | break 28 | } 29 | if removed { 30 | continue 'outer 31 | } 32 | } 33 | } 34 | } 35 | 36 | vec![first_uniq, second_uniq] 37 | } 38 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Based on the "trust" template v0.1.1 2 | # https://github.com/japaric/trust/tree/v0.1.1 3 | 4 | environment: 5 | global: 6 | RUST_VERSION: stable 7 | 8 | CRATE_NAME: array_tool 9 | 10 | matrix: 11 | # MinGW 12 | - TARGET: i686-pc-windows-gnu 13 | - TARGET: x86_64-pc-windows-gnu 14 | 15 | # MSVC 16 | - TARGET: i686-pc-windows-msvc 17 | - TARGET: x86_64-pc-windows-msvc 18 | 19 | # Testing other channels 20 | - TARGET: x86_64-pc-windows-gnu 21 | RUST_VERSION: nightly 22 | - TARGET: x86_64-pc-windows-msvc 23 | RUST_VERSION: nightly 24 | 25 | install: 26 | - ps: >- 27 | If ($Env:TARGET -eq 'x86_64-pc-windows-gnu') { 28 | $Env:PATH += ';C:\msys64\mingw64\bin' 29 | } ElseIf ($Env:TARGET -eq 'i686-pc-windows-gnu') { 30 | $Env:PATH += ';C:\msys64\mingw32\bin' 31 | } 32 | - curl -sSf -o rustup-init.exe https://win.rustup.rs/ 33 | - rustup-init.exe -y --default-host %TARGET% --default-toolchain %RUST_VERSION% 34 | - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin 35 | - rustc -Vv 36 | - cargo -V 37 | 38 | test_script: 39 | - if [%APPVEYOR_REPO_TAG%]==[false] ( 40 | cargo build --target %TARGET% && 41 | cargo build --target %TARGET% --release && 42 | cargo test --target %TARGET% && 43 | cargo test --target %TARGET% --release 44 | ) 45 | 46 | cache: 47 | - C:\Users\appveyor\.cargo\registry 48 | - target 49 | 50 | branches: 51 | only: 52 | # Release tags 53 | - /^v\d+\.\d+\.\d+.*$/ 54 | - master 55 | 56 | # Building is done in the test phase, so we disable Appveyor's build phase. 57 | build: false 58 | -------------------------------------------------------------------------------- /benches/bench.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | // NOTES 4 | // 5 | // string slices are faster than character iteration with method nth. 6 | 7 | extern crate test; 8 | use test::Bencher; 9 | extern crate array_tool; 10 | use array_tool::*; 11 | 12 | #[bench] 13 | fn uniques1(b: &mut Bencher) { 14 | let a: Vec = vec![ 15 | 1, 43, 6, 26, 62, 7, 27, 2, 3, 62, 246, 2346, 6, 7, 2, 35, 62, 6, 325, 56, 63, 25, 4, 8, 16 | 8727, 7, 74, 452, 17 | ]; 18 | let d: Vec = vec![ 19 | 36, 63, 74, 872, 2, 772, 7, 2, 54, 265, 3245, 45, 754, 235, 4567, 67, 23, 2, 542, 352, 20 | ]; 21 | b.iter(|| vec::Uniq::uniq(&a, d.clone())) 22 | } 23 | 24 | #[bench] 25 | fn sorted_uniques(b: &mut Bencher) { 26 | let a: Vec = vec![ 27 | 1, 2, 4, 7, 11, 12, 15, 15, 19, 22, 39, 50, 51, 52, 102, 104, 150, 230, 280, 400, 401, 402, 28 | 8231, 49823, 109482, 29 | ]; 30 | let d: Vec = vec![ 31 | 1, 2, 3, 6, 8, 11, 15, 19, 50, 102, 103, 108, 120, 160, 199, 220, 230, 280, 500, 509, 8231, 32 | 29391, 20413, 33 | ]; 34 | b.iter(|| sorted_vec::SortedUniq::uniq(&a, d.clone())) 35 | } 36 | 37 | #[bench] 38 | fn intersects(b: &mut Bencher) { 39 | let a: Vec = vec![ 40 | 1, 2, 4, 7, 11, 12, 15, 15, 19, 22, 39, 50, 51, 52, 102, 104, 150, 230, 280, 400, 401, 402, 41 | 8231, 49823, 109482, 42 | ]; 43 | let d: Vec = vec![ 44 | 1, 2, 3, 6, 8, 11, 15, 19, 50, 102, 103, 108, 120, 160, 199, 220, 230, 280, 500, 509, 8231, 45 | 29391, 20413, 46 | ]; 47 | 48 | b.iter(|| vec::Intersect::intersect(&a, d.clone())) 49 | } 50 | 51 | #[bench] 52 | fn sorted_intersects(b: &mut Bencher) { 53 | let a: Vec = vec![ 54 | 1, 2, 4, 7, 11, 12, 15, 15, 19, 22, 39, 50, 51, 52, 102, 104, 150, 230, 280, 400, 401, 402, 55 | 8231, 49823, 109482, 56 | ]; 57 | let d: Vec = vec![ 58 | 1, 2, 3, 6, 8, 11, 15, 19, 50, 102, 103, 108, 120, 160, 199, 220, 230, 280, 500, 509, 8231, 59 | 29391, 20413, 60 | ]; 61 | 62 | b.iter(|| sorted_vec::SortedIntersect::intersect(&a, d.clone())) 63 | } 64 | 65 | #[bench] 66 | fn times(b: &mut Bencher) { 67 | b.iter(|| { 68 | use array_tool::vec::Times; 69 | vec![1, 2, 3, 4, 5, 6].times(150); 70 | }); 71 | } 72 | 73 | #[bench] 74 | fn subst_marks(b: &mut Bencher) { 75 | b.iter(|| { 76 | use array_tool::string::SubstMarks; 77 | "dfgklerfgseawrfgawergq35g4w6uw4372472q4762q47yq35uw4567u32qy7q3yuq3" 78 | .subst_marks(vec![0, 3, 6, 9, 12, 24, 34, 40], "Z"); 79 | }); 80 | } 81 | 82 | #[bench] 83 | fn word_wrap(b: &mut Bencher) { 84 | b.iter(|| { 85 | use array_tool::string::WordWrap; 86 | "asdf sdf s df d sd\n sf sfg sg g\n sfdgsg\n gfdga a\n ".word_wrap(3); 87 | }); 88 | } 89 | -------------------------------------------------------------------------------- /src/iter.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | use std::iter::IntoIterator; 3 | 4 | #[doc(hidden)] 5 | #[derive(Clone, Debug)] 6 | #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] 7 | pub struct ZipOption { 8 | a: A, 9 | b: B, 10 | // index and len are only used by the specialized version of zip 11 | #[allow(dead_code)] 12 | index: usize, 13 | #[allow(dead_code)] 14 | len: usize, 15 | } 16 | 17 | /// Zips to iterators together to the longest length 18 | /// via Option<(Option, Option)> 19 | pub trait ZipOpt { 20 | /// Zip to iterators to longest length via Option<(Option, Option)> results. 21 | /// # Example 22 | /// ``` 23 | /// use array_tool::iter::ZipOpt; 24 | /// 25 | /// let a = vec!["a","b","c", "d"]; 26 | /// let b = vec!["c","d"]; 27 | /// let mut x = a.iter().zip_option(b.iter()); 28 | /// 29 | /// assert_eq!(x.next(), Some((Some(&"a"), Some(&"c")))); 30 | /// assert_eq!(x.next(), Some((Some(&"b"), Some(&"d")))); 31 | /// assert_eq!(x.next(), Some((Some(&"c"), None))); 32 | /// assert_eq!(x.next(), Some((Some(&"d"), None))); 33 | /// assert_eq!(x.next(), None); 34 | /// ``` 35 | /// 36 | /// # Output 37 | /// ```text 38 | /// vec![ "a", "b", "c", "d" ] 39 | /// ``` 40 | fn zip_option(self, other: U) -> ZipOption 41 | where 42 | Self: Sized, 43 | U: IntoIterator; 44 | } 45 | 46 | impl ZipOpt for I { 47 | #[inline] 48 | fn zip_option(self, other: U) -> ZipOption 49 | where 50 | Self: Sized, 51 | U: IntoIterator, 52 | { 53 | ZipOption::new(self, other.into_iter()) 54 | } 55 | } 56 | 57 | impl Iterator for ZipOption 58 | where 59 | A: Iterator, 60 | B: Iterator, 61 | { 62 | type Item = (Option, Option); 63 | 64 | #[inline] 65 | fn next(&mut self) -> Option { 66 | ZipImpl::next(self) 67 | } 68 | 69 | #[inline] 70 | fn size_hint(&self) -> (usize, Option) { 71 | ZipImpl::size_hint(self) 72 | } 73 | 74 | #[inline] 75 | fn nth(&mut self, n: usize) -> Option { 76 | ZipImpl::nth(self, n) 77 | } 78 | } 79 | 80 | #[doc(hidden)] 81 | impl DoubleEndedIterator for ZipOption 82 | where 83 | A: DoubleEndedIterator + ExactSizeIterator, 84 | B: DoubleEndedIterator + ExactSizeIterator, 85 | { 86 | #[inline] 87 | fn next_back(&mut self) -> Option<(Option, Option)> { 88 | ZipImpl::next_back(self) 89 | } 90 | } 91 | 92 | #[doc(hidden)] 93 | trait ZipImpl { 94 | type Item; 95 | fn new(a: A, b: B) -> Self; 96 | fn next(&mut self) -> Option; 97 | fn size_hint(&self) -> (usize, Option); 98 | fn nth(&mut self, n: usize) -> Option; 99 | fn super_nth(&mut self, mut n: usize) -> Option { 100 | while let Some(x) = self.next() { 101 | if n == 0 { 102 | return Some(x); 103 | } 104 | n -= 1; 105 | } 106 | None 107 | } 108 | fn next_back(&mut self) -> Option 109 | where 110 | A: DoubleEndedIterator + ExactSizeIterator, 111 | B: DoubleEndedIterator + ExactSizeIterator; 112 | } 113 | 114 | #[doc(hidden)] 115 | impl ZipImpl for ZipOption 116 | where 117 | A: Iterator, 118 | B: Iterator, 119 | { 120 | type Item = (Option, Option); 121 | fn new(a: A, b: B) -> Self { 122 | ZipOption { 123 | a, 124 | b, 125 | index: 0, // unused 126 | len: 0, // unused 127 | } 128 | } 129 | 130 | #[inline] 131 | fn next(&mut self) -> Option<(Option, Option)> { 132 | let first = self.a.next(); 133 | let second = self.b.next(); 134 | 135 | if first.is_some() || second.is_some() { 136 | Some((first, second)) 137 | } else { 138 | None 139 | } 140 | } 141 | 142 | #[inline] 143 | fn nth(&mut self, n: usize) -> Option { 144 | self.super_nth(n) 145 | } 146 | 147 | #[inline] 148 | fn next_back(&mut self) -> Option<(Option, Option)> 149 | where 150 | A: DoubleEndedIterator + ExactSizeIterator, 151 | B: DoubleEndedIterator + ExactSizeIterator, 152 | { 153 | let a_sz = self.a.len(); 154 | let b_sz = self.b.len(); 155 | if a_sz != b_sz { 156 | // Adjust a, b to equal length 157 | if a_sz > b_sz { 158 | for _ in 0..a_sz - b_sz { 159 | self.a.next_back(); 160 | } 161 | } else { 162 | for _ in 0..b_sz - a_sz { 163 | self.b.next_back(); 164 | } 165 | } 166 | } 167 | match (self.a.next_back(), self.b.next_back()) { 168 | (None, None) => None, 169 | (f, s) => Some((f, s)), 170 | } 171 | } 172 | 173 | #[inline] 174 | fn size_hint(&self) -> (usize, Option) { 175 | let (a_lower, a_upper) = self.a.size_hint(); 176 | let (b_lower, b_upper) = self.b.size_hint(); 177 | 178 | let lower = cmp::min(a_lower, b_lower); 179 | 180 | let upper = match (a_upper, b_upper) { 181 | (Some(x), Some(y)) => Some(cmp::max(x, y)), 182 | (Some(x), None) => Some(x), 183 | (None, Some(y)) => Some(y), 184 | (None, None) => None, 185 | }; 186 | 187 | (lower, upper) 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny( 2 | missing_docs, 3 | trivial_casts, 4 | trivial_numeric_casts, 5 | missing_debug_implementations, 6 | missing_copy_implementations, 7 | unsafe_code, 8 | unused_import_braces, 9 | unused_qualifications 10 | )] 11 | // Copyright 2015-2017 Daniel P. Clark & array_tool Developers 12 | // 13 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 16 | // copied, modified, or distributed except according to those terms. 17 | 18 | //! # Array Tool 19 | //! 20 | //! is a collection of powerful methods for working with collections. 21 | //! Some of the most common methods you would use on Arrays made available 22 | //! on Vectors. Polymorphic implementations for handling most of your use cases. 23 | //! 24 | //! In your rust files where you plan to use it put this at the top 25 | //! 26 | //! ``` 27 | //! extern crate array_tool; 28 | //! ``` 29 | //! 30 | //! And if you plan to use all of the Vector helper methods available: 31 | //! 32 | //! ``` 33 | //! use array_tool::vec::*; 34 | //! ``` 35 | //! 36 | //! This crate is not limited to just Vector methods and has some helpful 37 | //! string methods as well. 38 | 39 | /// Array Tool provides useful methods for iterators 40 | pub mod iter; 41 | /// Array Tool provides optimized runtimes for sorted vectors 42 | pub mod sorted_vec; 43 | /// A string is a collection so we should have more methods for handling strings. 44 | pub mod string; 45 | /// Array Tool provides many useful methods for vectors 46 | pub mod vec; 47 | 48 | /// Get `uniques` from two vectors 49 | /// 50 | /// # Example 51 | /// ``` 52 | /// use array_tool::uniques; 53 | /// 54 | /// uniques(vec![1,2,3,4,5], vec![2,5,6,7,8]); 55 | /// ``` 56 | /// 57 | /// # Output 58 | /// ```text 59 | /// vec![vec![1,3,4], vec![6,7,8]] 60 | /// ``` 61 | pub fn uniques(a: Vec, b: Vec) -> Vec> { 62 | use self::vec::Uniq; 63 | vec![a.uniq(b.clone()), b.uniq(a)] 64 | } 65 | -------------------------------------------------------------------------------- /src/sorted_vec.rs: -------------------------------------------------------------------------------- 1 | use vec::Uniq; 2 | /// Collection of methods for getting or evaluating uniqueness, assuming some 3 | /// kind of sorted-ness 4 | pub trait SortedUniq: Uniq { 5 | /// `uniq` returns a vector of unique values within itself as compared to the 6 | /// other vector, which is provided as an input parameter. Both of these must 7 | /// be sorted and have non-decreasing values 8 | /// 9 | /// # Example 10 | /// ``` 11 | /// use array_tool::sorted_vec::SortedUniq; 12 | /// 13 | /// vec![1,2,3,4,5,6].uniq(vec![1,2,5,7,9]); 14 | /// ``` 15 | /// 16 | /// # Output 17 | /// ```text 18 | /// vec![3,4,6] 19 | /// ``` 20 | fn uniq(&self, other: Self) -> Self; 21 | 22 | /// `uniq_desc` returns a vector of unique values within itself as compared 23 | /// to the other vector, which is provided as an input parameter. Both of these 24 | /// must be sorted and have non-increasing values. Inverse of `uniq`. 25 | /// 26 | /// # Example 27 | /// ``` 28 | /// use array_tool::sorted_vec::SortedUniq; 29 | /// 30 | /// vec![6,5,4,3,2,1].uniq_desc(vec![9,7,5,3,1]); 31 | /// ``` 32 | /// 33 | /// # Output 34 | /// ```text 35 | /// vec![6,4,2] 36 | /// ``` 37 | fn uniq_desc(&self, other: Self) -> Self; 38 | 39 | /// `unique` returns a vector like Self but with all duplicated elements 40 | /// removed. Self must be a vector sorted in some way. 41 | /// 42 | /// # Example 43 | /// ``` 44 | /// use array_tool::sorted_vec::SortedUniq; 45 | /// 46 | /// vec![1,1,1,2,3,3,4,5,6].unique(); 47 | /// ``` 48 | /// 49 | /// # Output 50 | /// ```text 51 | /// vec![1,2,3,4,5,6] 52 | /// ``` 53 | fn unique(&self) -> Self; 54 | 55 | /// `is_unique` returns boolean value on whether all values within Self are 56 | /// unique. Self must be a vecto sorted in some way. 57 | /// 58 | /// # Example 59 | /// ``` 60 | /// use array_tool::sorted_vec::SortedUniq; 61 | /// 62 | /// vec![1,2,4,6,6,7,8].is_unique(); 63 | /// ``` 64 | /// 65 | /// # Output 66 | /// ```text 67 | /// false 68 | /// ``` 69 | fn is_unique(&self) -> bool; 70 | 71 | /// `uniq_via` returns a vector of unique values within itself as compared to 72 | /// the other vector which is provided as an input parameter, as defined by 73 | /// the two provided custom comparators. Both vectors must be sorted in a way 74 | /// corresponding to the custom comparators, and the sort direction is determined 75 | /// by the second comparator: l < r for increasing. 76 | /// 77 | /// # Example 78 | /// ``` 79 | /// use array_tool::sorted_vec::SortedUniq; 80 | /// 81 | /// vec![1,2,3,4,5,6].uniq_via( 82 | /// vec![1,2,5,7,9], 83 | /// |l, r| l == r, 84 | /// |l, r| l < r 85 | /// ); 86 | /// ``` 87 | /// 88 | /// # Output 89 | /// ```text 90 | /// vec![3, 4, 6] 91 | /// ``` 92 | fn uniq_via bool, K: Fn(&T, &T) -> bool>( 93 | &self, 94 | other: Self, 95 | eq: F, 96 | ord: K, 97 | ) -> Self; 98 | 99 | /// `unique_via` removes duplicates, as defined by a provided custom comparator, 100 | /// from within the vector and returns Self. Self must be a vector sorted in a 101 | /// way corresponding to the custom comparator. 102 | /// 103 | /// # Example 104 | /// ``` 105 | /// use array_tool::sorted_vec::SortedUniq; 106 | /// 107 | /// vec![1,2,3,3,4,5,6].unique_via(|l, r| l == r); 108 | /// ``` 109 | /// 110 | /// # Output 111 | /// ```text 112 | /// vec![1,2,3,4,5,6] 113 | /// ``` 114 | fn unique_via bool>(&self, eq: F) -> Self; 115 | 116 | /// `is_unique_via` returns boolean value on whether all values within 117 | /// Self are unique, as defined by a provided custom comparator. Self must 118 | /// be a vector sorted in some way corresponding to the custom comparator. 119 | /// 120 | /// # Example 121 | /// ``` 122 | /// use array_tool::sorted_vec::SortedUniq; 123 | /// 124 | /// vec![1.0,2.0,2.4,3.3,3.1,3.5,4.6,5.2,6.2].is_unique_via( |l: &f64, r: &f64| l.floor() == r.floor() ); 125 | /// ``` 126 | /// 127 | /// # Output 128 | /// ```text 129 | /// false 130 | /// ``` 131 | fn is_unique_via bool>(&self, eq: F) -> bool; 132 | } 133 | 134 | impl SortedUniq for Vec { 135 | fn uniq(&self, other: Self) -> Self { 136 | SortedUniq::::uniq_via(self, other, |l, r| l == r, |l, r| l < r) 137 | } 138 | 139 | fn uniq_desc(&self, other: Self) -> Self { 140 | SortedUniq::::uniq_via(self, other, |l, r| l == r, |l, r| l > r) 141 | } 142 | 143 | fn unique(&self) -> Self { 144 | SortedUniq::::unique_via(self, |l, r| l == r) 145 | } 146 | 147 | fn is_unique(&self) -> bool { 148 | SortedUniq::::is_unique_via(self, |l, r| l == r) 149 | } 150 | 151 | fn uniq_via bool, K: Fn(&T, &T) -> bool>( 152 | &self, 153 | other: Self, 154 | eq: F, 155 | ord: K, 156 | ) -> Self { 157 | let mut out = self.clone(); 158 | let mut cursor: usize = 0; 159 | let mut i: usize = 0; 160 | let mut j: usize = 0; 161 | while i < out.len() && j < other.len() { 162 | if ord(&other[j], &out[i]) { 163 | // ^ should other[j] be ordered before out[i]? 164 | j += 1; 165 | } else if eq(&other[j], &out[i]) { 166 | i += 1; 167 | j += 1; 168 | } else { 169 | if i == 0 || !eq(&out[i - 1], &out[i]) { 170 | out[cursor] = out[i]; 171 | cursor += 1; 172 | } 173 | i += 1; 174 | } 175 | } 176 | while i < out.len() { 177 | if i == 0 || !eq(&out[i - 1], &out[i]) { 178 | out[cursor] = out[i]; 179 | cursor += 1; 180 | } 181 | i += 1; 182 | } 183 | out.truncate(cursor); 184 | out 185 | } 186 | fn unique_via bool>(&self, eq: F) -> Self { 187 | let mut out = self.clone(); 188 | let mut cursor: usize = 1; 189 | for i in 1..out.len() { 190 | if !eq(&out[i], &out[i - 1]) { 191 | if i != cursor { 192 | out[cursor] = out[i]; 193 | } 194 | cursor += 1; 195 | } 196 | } 197 | out.truncate(cursor); 198 | out 199 | } 200 | 201 | fn is_unique_via bool>(&self, eq: F) -> bool { 202 | for i in 1..self.len() { 203 | if eq(&self[i], &self[i - 1]) { 204 | return false; 205 | } 206 | } 207 | true 208 | } 209 | } 210 | 211 | use vec::Intersect; 212 | /// Sorted Set Intersection — Returns a new array containing elements common to the 213 | /// two arrays, excluding any duplicates. The order is preserved from the original 214 | /// array. 215 | pub trait SortedIntersect: Intersect { 216 | /// Performs basic intersect operation with given other vector, returning a copy 217 | /// of Self with elements common to both vectors. Both vectors have to be sorted 218 | /// with ascending values. 219 | /// 220 | /// # Example 221 | /// ``` 222 | /// use array_tool::sorted_vec::SortedIntersect; 223 | /// 224 | /// vec![1,1,3,5].intersect(vec![1,2,3]); 225 | /// ``` 226 | /// 227 | /// # Output 228 | /// ```text 229 | /// vec![1,3] 230 | /// ``` 231 | fn intersect(&self, other: Self) -> Self; 232 | 233 | /// Performs basic intersect operation with given other vector, returning a copy 234 | /// of Self with elements common to both vectors. Both vectors have to be sorted 235 | /// with descending values. 236 | /// # Example 237 | /// ``` 238 | /// use array_tool::sorted_vec::SortedIntersect; 239 | /// 240 | /// vec![5,4,3,2,1].intersect_desc(vec![3,2,1]); 241 | /// ``` 242 | /// 243 | /// # Output 244 | /// ```text 245 | /// vec![3,2,1] 246 | /// ``` 247 | fn intersect_desc(&self, other: Self) -> Self; 248 | 249 | /// Performs intersect operation with given other vector and two custom comparators 250 | /// provided as arguments. Vectors must be sorted in some way corresponding to the 251 | /// first equality comparator, and second order comparator. If ascending: `|l, r| 252 | /// l < r` 253 | /// 254 | /// # Example 255 | /// ``` 256 | /// use array_tool::sorted_vec::SortedIntersect; 257 | /// 258 | /// vec!['a','a','c','e'].intersect_if( 259 | /// vec!['A','B','C'], 260 | /// |l, r| l.eq_ignore_ascii_case(r), 261 | /// |l, r| l == &r.to_ascii_lowercase(), 262 | /// ); 263 | /// ``` 264 | /// 265 | /// # Output 266 | /// ```text 267 | /// vec!['a','c'] 268 | /// ``` 269 | fn intersect_if bool, O: Fn(&T, &T) -> bool>( 270 | &self, 271 | other: Self, 272 | eq: E, 273 | ord: O, 274 | ) -> Self; 275 | } 276 | 277 | impl SortedIntersect for Vec { 278 | fn intersect(&self, other: Self) -> Self { 279 | SortedIntersect::::intersect_if(self, other, |l, r| l == r, |l, r| l < r) 280 | } 281 | 282 | fn intersect_desc(&self, other: Self) -> Self { 283 | SortedIntersect::::intersect_if(self, other, |l, r| l == r, |l, r| l > r) 284 | } 285 | 286 | fn intersect_if bool, O: Fn(&T, &T) -> bool>( 287 | &self, 288 | other: Self, 289 | eq: E, 290 | ord: O, 291 | ) -> Self { 292 | let mut out = self.clone(); 293 | let (mut i, mut j, mut cursor) = (0, 0, 0); 294 | while i < out.len() && j < other.len() { 295 | if eq(&out[i], &other[j]) { 296 | out[cursor] = out[i]; 297 | cursor += 1; 298 | j += 1; 299 | i += 1; 300 | } else if ord(&out[i], &other[j]) { 301 | // usually: out[i] < other[j] 302 | i += 1; 303 | } else { 304 | j += 1; 305 | } 306 | } 307 | out.truncate(cursor); 308 | out 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /src/string.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2017 Daniel P. Clark & array_tool Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | /// A grapheme iterator that produces the bytes for each grapheme. 9 | #[derive(Debug)] 10 | pub struct GraphemeBytesIter<'a> { 11 | source: &'a str, 12 | offset: usize, 13 | grapheme_count: usize, 14 | } 15 | impl<'a> GraphemeBytesIter<'a> { 16 | /// Creates a new grapheme iterator from a string source. 17 | pub fn new(source: &'a str) -> GraphemeBytesIter<'a> { 18 | GraphemeBytesIter { 19 | source, 20 | offset: 0, 21 | grapheme_count: 0, 22 | } 23 | } 24 | } 25 | impl<'a> Iterator for GraphemeBytesIter<'a> { 26 | type Item = &'a [u8]; 27 | 28 | fn next(&mut self) -> Option<&'a [u8]> { 29 | let mut result: Option<&[u8]> = None; 30 | let mut idx = self.offset; 31 | for _ in self.offset..self.source.len() { 32 | idx += 1; 33 | if self.offset < self.source.len() { 34 | if self.source.is_char_boundary(idx) { 35 | let slice: &[u8] = self.source[self.offset..idx].as_bytes(); 36 | 37 | self.grapheme_count += 1; 38 | self.offset = idx; 39 | 40 | result = Some(slice); 41 | break; 42 | } 43 | } 44 | } 45 | result 46 | } 47 | } 48 | impl<'a> ExactSizeIterator for GraphemeBytesIter<'a> { 49 | fn len(&self) -> usize { 50 | self.source.chars().count() 51 | } 52 | } 53 | /// ToGraphemeBytesIter - create an iterator to return bytes for each grapheme in a string. 54 | pub trait ToGraphemeBytesIter<'a> { 55 | /// Returns a GraphemeBytesIter which you may iterate over. 56 | /// 57 | /// # Example 58 | /// ``` 59 | /// use array_tool::string::ToGraphemeBytesIter; 60 | /// 61 | /// let string = "a s—d féZ"; 62 | /// let mut graphemes = string.grapheme_bytes_iter(); 63 | /// graphemes.skip(3).next(); 64 | /// ``` 65 | /// 66 | /// # Output 67 | /// ```text 68 | /// [226, 128, 148] 69 | /// ``` 70 | fn grapheme_bytes_iter(&'a self) -> GraphemeBytesIter<'a>; 71 | } 72 | impl<'a> ToGraphemeBytesIter<'a> for str { 73 | fn grapheme_bytes_iter(&'a self) -> GraphemeBytesIter<'a> { 74 | GraphemeBytesIter::new(&self) 75 | } 76 | } 77 | 78 | /// Squeeze - squeezes duplicate characters down to one each 79 | pub trait Squeeze { 80 | /// # Example 81 | /// ``` 82 | /// use array_tool::string::Squeeze; 83 | /// 84 | /// "yellow moon".squeeze(""); 85 | /// ``` 86 | /// 87 | /// # Output 88 | /// ```text 89 | /// "yelow mon" 90 | /// ``` 91 | fn squeeze(&self, targets: &'static str) -> String; 92 | } 93 | impl Squeeze for str { 94 | fn squeeze(&self, targets: &'static str) -> String { 95 | let mut output = Vec::::with_capacity(self.len()); 96 | let everything: bool = targets.is_empty(); 97 | let chars = targets.grapheme_bytes_iter().collect::>(); 98 | let mut last: &[u8] = &[0]; 99 | for character in self.grapheme_bytes_iter() { 100 | if last != character { 101 | output.extend_from_slice(character); 102 | } else if !(everything || chars.contains(&character)) { 103 | output.extend_from_slice(character); 104 | } 105 | last = character; 106 | } 107 | String::from_utf8(output).expect("squeeze failed to render String!") 108 | } 109 | } 110 | 111 | /// Justify - expand line to given width. 112 | pub trait Justify { 113 | /// # Example 114 | /// ``` 115 | /// use array_tool::string::Justify; 116 | /// 117 | /// "asd asdf asd".justify_line(14); 118 | /// ``` 119 | /// 120 | /// # Output 121 | /// ```text 122 | /// "asd asdf asd" 123 | /// ``` 124 | fn justify_line(&self, width: usize) -> String; 125 | } 126 | 127 | impl Justify for str { 128 | fn justify_line(&self, width: usize) -> String { 129 | if self.is_empty() { 130 | return format!("{}", self); 131 | }; 132 | let trimmed = self.trim(); 133 | let len = trimmed.chars().count(); 134 | if len >= width { 135 | return self.to_string(); 136 | }; 137 | let difference = width - len; 138 | let iter = trimmed.split_whitespace(); 139 | let spaces = iter.count() - 1; 140 | let mut iter = trimmed.split_whitespace().peekable(); 141 | if spaces == 0 { 142 | return self.to_string(); 143 | } 144 | let mut obj = String::with_capacity(trimmed.len() + spaces); 145 | 146 | let div = difference / spaces; 147 | let mut remainder = difference % spaces; 148 | 149 | while let Some(x) = iter.next() { 150 | obj.push_str(x); 151 | let val = if remainder > 0 { 152 | remainder = remainder - 1; 153 | div + 1 154 | } else { 155 | div 156 | }; 157 | for _ in 0..val + 1 { 158 | if let Some(_) = iter.peek() { 159 | // Don't add spaces if last word 160 | obj.push_str(" "); 161 | } 162 | } 163 | } 164 | obj 165 | } 166 | } 167 | 168 | /// Substitute string character for each index given. 169 | pub trait SubstMarks { 170 | /// # Example 171 | /// ``` 172 | /// use array_tool::string::SubstMarks; 173 | /// 174 | /// "asdf asdf asdf".subst_marks(vec![0,5,8], "Z"); 175 | /// ``` 176 | /// 177 | /// # Output 178 | /// ```text 179 | /// "Zsdf ZsdZ asdf" 180 | /// ``` 181 | fn subst_marks(&self, marks: Vec, chr: &'static str) -> String; 182 | } 183 | impl SubstMarks for str { 184 | fn subst_marks(&self, marks: Vec, chr: &'static str) -> String { 185 | let mut output = Vec::::with_capacity(self.len()); 186 | let mut count = 0; 187 | let mut last = 0; 188 | for i in 0..self.len() { 189 | let idx = i + 1; 190 | if self.is_char_boundary(idx) { 191 | if marks.contains(&count) { 192 | count += 1; 193 | last = idx; 194 | output.extend_from_slice(chr.as_bytes()); 195 | continue; 196 | } 197 | 198 | let slice: &[u8] = self[last..idx].as_bytes(); 199 | output.extend_from_slice(slice); 200 | 201 | count += 1; 202 | last = idx 203 | } 204 | } 205 | String::from_utf8(output).expect("subst_marks failed to render String!") 206 | } 207 | } 208 | 209 | /// After whitespace 210 | pub trait AfterWhitespace { 211 | /// Given offset method will seek from there to end of string to find the first 212 | /// non white space. Resulting value is counted from offset. 213 | /// 214 | /// # Example 215 | /// ``` 216 | /// use array_tool::string::AfterWhitespace; 217 | /// 218 | /// assert_eq!( 219 | /// "asdf asdf asdf".seek_end_of_whitespace(6), 220 | /// Some(9) 221 | /// ); 222 | /// ``` 223 | fn seek_end_of_whitespace(&self, offset: usize) -> Option; 224 | } 225 | impl AfterWhitespace for str { 226 | fn seek_end_of_whitespace(&self, offset: usize) -> Option { 227 | if self.len() < offset { 228 | return None; 229 | }; 230 | let mut seeker = self[offset..self.len()].chars(); 231 | let mut val = None; 232 | let mut indx = 0; 233 | while let Some(x) = seeker.next() { 234 | if x.ne(&" ".chars().next().unwrap()) { 235 | val = Some(indx); 236 | break; 237 | } 238 | indx += 1; 239 | } 240 | val 241 | } 242 | } 243 | 244 | /// Word wrapping 245 | pub trait WordWrap { 246 | /// White space is treated as valid content and new lines will only be swapped in for 247 | /// the last white space character at the end of the given width. White space may reach beyond 248 | /// the width you've provided. You will need to trim end of lines in your own output (e.g. 249 | /// splitting string at each new line and printing the line with trim_right). Or just trust 250 | /// that lines that are beyond the width are just white space and only print the width - 251 | /// ignoring tailing white space. 252 | /// 253 | /// # Example 254 | /// ``` 255 | /// use array_tool::string::WordWrap; 256 | /// 257 | /// "asd asdf asd".word_wrap(8); 258 | /// ``` 259 | /// 260 | /// # Output 261 | /// ```text 262 | /// "asd asdf\nasd" 263 | /// ``` 264 | fn word_wrap(&self, width: usize) -> String; 265 | } 266 | // No need to worry about character encoding since we're only checking for the 267 | // space and new line characters. 268 | impl WordWrap for &'static str { 269 | fn word_wrap(&self, width: usize) -> String { 270 | let mut markers = vec![]; 271 | fn wordwrap( 272 | t: &'static str, 273 | chunk: usize, 274 | offset: usize, 275 | mrkrs: &mut Vec, 276 | ) -> String { 277 | match t[offset..*vec![offset + chunk, t.len()].iter().min().unwrap()].rfind("\n") { 278 | None => { 279 | match t[offset..*vec![offset + chunk, t.len()].iter().min().unwrap()].rfind(" ") 280 | { 281 | Some(x) => { 282 | let mut eows = x; // end of white space 283 | if offset + chunk < t.len() { 284 | // check if white space continues 285 | match t.seek_end_of_whitespace(offset + x) { 286 | Some(a) => { 287 | if a.ne(&0) { 288 | eows = x + a - 1; 289 | } 290 | } 291 | None => {} 292 | } 293 | } 294 | if offset + chunk < t.len() { 295 | // safe to seek ahead by 1 or not end of string 296 | if !["\n".chars().next().unwrap(), " ".chars().next().unwrap()] 297 | .contains( 298 | &t[offset + eows + 1..offset + eows + 2] 299 | .chars() 300 | .next() 301 | .unwrap(), 302 | ) 303 | { 304 | mrkrs.push(offset + eows) 305 | } 306 | }; 307 | wordwrap(t, chunk, offset + eows + 1, mrkrs) 308 | } 309 | None => { 310 | if offset + chunk < t.len() { 311 | // String may continue 312 | wordwrap(t, chunk, offset + 1, mrkrs) // Recurse + 1 until next space 313 | } else { 314 | return t.subst_marks(mrkrs.to_vec(), "\n"); 315 | } 316 | } 317 | } 318 | } 319 | Some(x) => wordwrap(t, chunk, offset + x + 1, mrkrs), 320 | } 321 | } 322 | wordwrap(self, width + 1, 0, &mut markers) 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /src/vec.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2017 Daniel P. Clark & array_tool Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | /// Several different methods for getting, or evaluating, uniqueness. 9 | pub trait Uniq { 10 | /// `uniq` returns a vector of unique values within itself as compared to 11 | /// the other vector which is provided as an input parameter. 12 | /// 13 | /// # Example 14 | /// ``` 15 | /// use array_tool::vec::Uniq; 16 | /// 17 | /// vec![1,2,3,4,5,6].uniq( vec![1,2,5,7,9] ); 18 | /// ``` 19 | /// 20 | /// # Output 21 | /// ```text 22 | /// vec![3,4,6] 23 | /// ``` 24 | fn uniq(&self, other: Self) -> Self; 25 | 26 | /// `unique` returns a vector like Self but with all duplicated elements removed. 27 | /// 28 | /// # Example 29 | /// ``` 30 | /// use array_tool::vec::Uniq; 31 | /// 32 | /// vec![1,2,1,3,2,3,4,5,6].unique(); 33 | /// ``` 34 | /// 35 | /// # Output 36 | /// ```text 37 | /// vec![1,2,3,4,5,6] 38 | /// ``` 39 | fn unique(&self) -> Self; 40 | 41 | /// `is_unique` returns boolean value on whether all values within 42 | /// Self are unique. 43 | /// 44 | /// # Example 45 | /// ``` 46 | /// use array_tool::vec::Uniq; 47 | /// 48 | /// vec![1,2,1,3,4,3,4,5,6].is_unique(); 49 | /// ``` 50 | /// 51 | /// # Output 52 | /// ```text 53 | /// false 54 | /// ``` 55 | fn is_unique(&self) -> bool; 56 | 57 | /// `uniq_via` returns a vector of unique values within itself as compared to 58 | /// the other vector which is provided as an input parameter, as defined by a 59 | /// provided custom comparator. 60 | /// 61 | /// # Example 62 | /// ``` 63 | /// use array_tool::vec::Uniq; 64 | /// 65 | /// vec![1,2,3,4,5,6].uniq_via( vec![1,2,5,7,9], |&l, r| l == r + 2 ); 66 | /// ``` 67 | /// 68 | /// # Output 69 | /// ```text 70 | /// vec![1,2,4,6] 71 | /// ``` 72 | fn uniq_via bool>(&self, other: Self, f: F) -> Self; 73 | 74 | /// `unique_via` removes duplicates, as defined by a provided custom comparator, 75 | /// from within the vector and returns Self. 76 | /// 77 | /// # Example 78 | /// ``` 79 | /// use array_tool::vec::Uniq; 80 | /// 81 | /// vec![1.0,2.0,1.4,3.3,2.1,3.5,4.6,5.2,6.2].unique_via( |l: &f64, r: &f64| l.floor() == r.floor() ); 82 | /// ``` 83 | /// 84 | /// # Output 85 | /// ```text 86 | /// vec![1.0,2.0,3.3,4.6,5.2,6.2] 87 | /// ``` 88 | fn unique_via bool>(&self, f: F) -> Self; 89 | 90 | /// `is_unique_via` returns boolean value on whether all values within 91 | /// Self are unique, as defined by a provided custom comparator. 92 | /// 93 | /// # Example 94 | /// ``` 95 | /// use array_tool::vec::Uniq; 96 | /// 97 | /// vec![1.0,2.0,1.4,3.3,2.1,3.5,4.6,5.2,6.2].is_unique_via( |l: &f64, r: &f64| l.floor() == r.floor() ); 98 | /// ``` 99 | /// 100 | /// # Output 101 | /// ```text 102 | /// false 103 | /// ``` 104 | fn is_unique_via bool>(&self, f: F) -> bool; 105 | } 106 | 107 | impl Uniq for Vec { 108 | fn uniq(&self, other: Vec) -> Vec { 109 | self.uniq_via(other, |lhs, rhs| lhs == rhs) 110 | } 111 | fn unique(&self) -> Vec { 112 | self.unique_via(|lhs, rhs| lhs == rhs) 113 | } 114 | fn is_unique(&self) -> bool { 115 | self.is_unique_via(|lhs, rhs| lhs == rhs) 116 | } 117 | 118 | fn uniq_via bool>(&self, other: Vec, f: F) -> Vec { 119 | let mut out = self.unique(); 120 | for x in other.unique() { 121 | for y in (0..out.len()).rev() { 122 | if f(&x, &out[y]) { 123 | out.remove(y); 124 | } 125 | } 126 | } 127 | out 128 | } 129 | fn unique_via bool>(&self, f: F) -> Vec { 130 | let mut a = self.clone(); 131 | for x in (0..a.len()).rev() { 132 | for y in (x + 1..a.len()).rev() { 133 | if f(&a[x], &a[y]) { 134 | a.remove(y); 135 | } 136 | } 137 | } 138 | a 139 | } 140 | fn is_unique_via bool>(&self, f: F) -> bool { 141 | let mut a = true; 142 | for x in 0..self.len() { 143 | for y in x + 1..self.len() { 144 | if f(&self[x], &self[y]) { 145 | a = false; 146 | break; 147 | } 148 | } 149 | } 150 | a 151 | } 152 | } 153 | 154 | /// Removes, or Adds, the first element of self. 155 | pub trait Shift { 156 | /// Removes and returns the first item from the vector 157 | /// 158 | /// # Example 159 | /// ``` 160 | /// use array_tool::vec::Shift; 161 | /// 162 | /// let mut x = vec![0,1,2,3]; 163 | /// assert_eq!(x.shift(), Some(0)); 164 | /// assert_eq!(x, vec![1,2,3]); 165 | /// ``` 166 | fn shift(&mut self) -> Option; 167 | /// Insert item at the beginning of the vector. No return value. 168 | /// 169 | /// # Example 170 | /// ``` 171 | /// use array_tool::vec::Shift; 172 | /// 173 | /// let mut x = vec![1,2,3]; 174 | /// x.unshift(0); 175 | /// assert_eq!(x, vec![0,1,2,3]); 176 | /// ``` 177 | fn unshift(&mut self, other: T); 178 | } 179 | impl Shift for Vec { 180 | fn shift(&mut self) -> Option { 181 | if self.len() == 0 { 182 | return None; 183 | } 184 | Some(self.remove(0)) 185 | } 186 | fn unshift(&mut self, other: T) { 187 | let _ = &self.insert(0, other); 188 | } 189 | } 190 | 191 | /// Set Intersection — Returns a new array containing elements common to the two 192 | /// arrays, excluding any duplicates. The order is preserved from the original array. 193 | pub trait Intersect { 194 | /// # Example 195 | /// ``` 196 | /// use array_tool::vec::Intersect; 197 | /// 198 | /// vec![1,1,3,5].intersect(vec![1,2,3]); 199 | /// ``` 200 | /// 201 | /// # Output 202 | /// ```text 203 | /// vec![1,3] 204 | /// ``` 205 | fn intersect(&self, other: Self) -> Self; 206 | /// # Example 207 | /// ``` 208 | /// # use std::ascii::AsciiExt; 209 | /// use array_tool::vec::Intersect; 210 | /// 211 | /// vec!['a','a','c','e'].intersect_if(vec!['A','B','C'], |l, r| l.eq_ignore_ascii_case(r)); 212 | /// ``` 213 | /// 214 | /// # Output 215 | /// ```text 216 | /// vec!['a','c'] 217 | /// ``` 218 | fn intersect_if bool>(&self, other: Self, validator: F) -> Self; 219 | } 220 | impl Intersect for Vec { 221 | fn intersect(&self, other: Vec) -> Vec { 222 | self.intersect_if(other, |l, r| l == r) 223 | } 224 | fn intersect_if bool>(&self, other: Self, validator: F) -> Self { 225 | let mut out = vec![]; 226 | let a = self.unique(); 227 | let length = other.len(); 228 | for x in a { 229 | for y in 0..length { 230 | if validator(&x, &other[y]) { 231 | out.push(x); 232 | break; 233 | } 234 | } 235 | } 236 | out 237 | } 238 | } 239 | 240 | /// Join vector of ToString capable things to a String with given delimiter. 241 | pub trait Join { 242 | /// # Example 243 | /// ``` 244 | /// use array_tool::vec::Join; 245 | /// 246 | /// vec![1,2,3].join(","); 247 | /// ``` 248 | /// 249 | /// # Output 250 | /// ```text 251 | /// "1,2,3" 252 | /// ``` 253 | fn join(&self, joiner: &'static str) -> String; 254 | } 255 | impl Join for Vec { 256 | fn join(&self, joiner: &'static str) -> String { 257 | let mut out = String::from(""); 258 | for x in 0..self.len() { 259 | out.push_str(&self[x].to_string()); 260 | if x < self.len() - 1 { 261 | out.push_str(&joiner) 262 | } 263 | } 264 | out 265 | } 266 | } 267 | 268 | /// Expand and duplicate the vectors content `times` the integer given 269 | pub trait Times { 270 | /// # Example 271 | /// ``` 272 | /// use array_tool::vec::Times; 273 | /// 274 | /// vec![1,2,3].times(3); 275 | /// ``` 276 | /// 277 | /// # Output 278 | /// ```text 279 | /// vec![1,2,3,1,2,3,1,2,3] 280 | /// ``` 281 | fn times(&self, qty: i32) -> Self; 282 | } 283 | impl Times for Vec { 284 | fn times(&self, qty: i32) -> Vec { 285 | if self.is_empty() { 286 | return vec![]; 287 | }; 288 | let mut out = vec![self[0].clone(); self.len() * (qty as usize)]; 289 | let mut cycle = self.iter().cycle(); 290 | for x in 0..self.len() * (qty as usize) { 291 | out[x] = cycle.next().unwrap().clone(); 292 | } 293 | out 294 | } 295 | } 296 | 297 | /// Create a `union` between two vectors. 298 | /// Returns a new vector by joining with other, excluding any duplicates and preserving 299 | /// the order from the original vector. 300 | pub trait Union { 301 | /// # Example 302 | /// ``` 303 | /// use array_tool::vec::Union; 304 | /// 305 | /// vec!["a","b","c"].union(vec!["c","d","a"]); 306 | /// ``` 307 | /// 308 | /// # Output 309 | /// ```text 310 | /// vec![ "a", "b", "c", "d" ] 311 | /// ``` 312 | fn union(&self, other: Self) -> Self; 313 | } 314 | impl Union for Vec { 315 | fn union(&self, other: Vec) -> Vec { 316 | let mut stack = self.clone(); 317 | for x in other { 318 | // don't use append method as it's destructive 319 | stack.push(x) 320 | } 321 | stack.unique() 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /tests/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate array_tool; 2 | 3 | #[test] 4 | fn it_implements_uniques() { 5 | assert_eq!( 6 | array_tool::uniques(vec![1, 2, 3, 4, 5, 6], vec![1, 2]), 7 | vec![vec![3, 4, 5, 6], vec![]] 8 | ); 9 | assert_eq!( 10 | array_tool::uniques(vec![1, 2, 3, 4, 5, 6], vec![1, 2, 3, 4]), 11 | vec![vec![5, 6], vec![]] 12 | ); 13 | assert_eq!( 14 | array_tool::uniques(vec![1, 2, 3], vec![1, 2, 3, 4, 5]), 15 | vec![vec![], vec![4, 5]] 16 | ); 17 | assert_eq!( 18 | array_tool::uniques(vec![1, 2, 9], vec![1, 2, 3, 4, 5]), 19 | vec![vec![9], vec![3, 4, 5]] 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /tests/sorted_vec.rs: -------------------------------------------------------------------------------- 1 | extern crate array_tool; 2 | 3 | #[test] 4 | fn it_implements_individual_uniq_on_sorted_vec() { 5 | use array_tool::sorted_vec::SortedUniq; 6 | assert_eq!( 7 | vec![1, 2, 3, 4, 5, 6].uniq(vec![1, 2, 5, 7, 9]), 8 | vec![3, 4, 6] 9 | ); 10 | assert_eq!(vec![1, 2, 3, 4, 5, 6].uniq(vec![6, 7]), vec![1, 2, 3, 4, 5]); 11 | } 12 | 13 | #[test] 14 | fn it_doesnt_mutate_on_individual_sorted_uniq() { 15 | use array_tool::sorted_vec::SortedUniq; 16 | let a = vec![1, 2, 3, 4, 5, 6]; 17 | a.uniq(vec![0, 2, 5, 6, 8]); 18 | assert_eq!(a, vec![1, 2, 3, 4, 5, 6]); 19 | } 20 | 21 | #[test] 22 | fn it_implements_individual_uniq_on_descending_sorted_vec() { 23 | use array_tool::sorted_vec::SortedUniq; 24 | assert_eq!( 25 | vec![6, 5, 4, 3, 2, 1].uniq_desc(vec![9, 7, 5, 3, 1]), 26 | vec![6, 4, 2] 27 | ); 28 | } 29 | 30 | #[test] 31 | fn it_can_return_its_own_sorted_unique() { 32 | use array_tool::sorted_vec::SortedUniq; 33 | assert_eq!( 34 | vec![1, 2, 2, 3, 4, 8, 8, 9].unique(), 35 | vec![1, 2, 3, 4, 8, 9] 36 | ); 37 | assert_eq!( 38 | vec![1, 2, 2, 3, 3, 3, 6, 7, 8, 8, 8, 8, 9, 11, 11, 11, 11, 11, 11].unique(), 39 | vec![1, 2, 3, 6, 7, 8, 9, 11] 40 | ); 41 | } 42 | 43 | #[test] 44 | fn it_answers_about_sorted_uniqueness() { 45 | use array_tool::vec::Uniq; 46 | assert_eq!(vec![1, 2, 3, 4].is_unique(), true); 47 | assert_eq!(vec![1, 2, 2, 4].is_unique(), false); 48 | } 49 | 50 | #[test] 51 | fn it_implements_individual_uniq_on_sorted_vec_via() { 52 | use array_tool::sorted_vec::SortedUniq; 53 | assert_eq!( 54 | vec![1.1, 2.6, 3.7, 4.7, 5.4, 5.2, 6.6].uniq_via( 55 | vec![1.5, 2.7, 5.1, 7.1, 9.4, 10.2], 56 | |l: &f64, r: &f64| l.floor() == r.floor(), 57 | |l, r| l.floor() < r.floor() 58 | ), 59 | vec![3.7, 4.7, 6.6] 60 | ); 61 | assert_eq!( 62 | vec![5, 4, 3, 2, 1].uniq_via(vec![8, 6, 4, 2], |l, r| l == r, |l, r| l > r), 63 | vec![5, 3, 1] 64 | ); 65 | } 66 | 67 | #[test] 68 | fn it_can_return_its_own_sorted_unique_via() { 69 | use array_tool::sorted_vec::SortedUniq; 70 | assert_eq!( 71 | vec![1.2, 2.5, 2.9, 2.9, 4.1, 4.4].unique_via(|l: &f64, r: &f64| l.floor() == r.floor()), 72 | vec![1.2, 2.5, 4.1] 73 | ); 74 | } 75 | 76 | #[test] 77 | fn it_answers_about_sorted_uniqueness_via() { 78 | use array_tool::sorted_vec::SortedUniq; 79 | assert_eq!( 80 | vec![1.2, 2.5, 3.3, 4.4].is_unique_via(|l: &f64, r: &f64| l.floor() == r.floor()), 81 | true 82 | ); 83 | assert_eq!( 84 | vec![1.1, 2.2, 2.7, 3.3].is_unique_via(|l: &f64, r: &f64| l.floor() == r.floor()), 85 | false 86 | ); 87 | } 88 | 89 | #[test] 90 | fn it_intersects() { 91 | use array_tool::sorted_vec::SortedIntersect; 92 | assert_eq!(vec![1, 1, 3, 5].intersect(vec![1, 2, 3]), vec![1, 3]); 93 | } 94 | 95 | #[test] 96 | fn it_intersects_descending_arrays() { 97 | use array_tool::sorted_vec::SortedIntersect; 98 | assert_eq!( 99 | vec![6, 5, 4, 3, 2, 1].intersect_desc(vec![4, 2, 0]), 100 | vec![4, 2] 101 | ); 102 | } 103 | 104 | #[test] 105 | fn it_intersects_if() { 106 | use array_tool::sorted_vec::SortedIntersect; 107 | assert_eq!( 108 | vec![1, 2, 3, 4, 5, 6, 7, 8].intersect_if( 109 | vec![2, 4, 6, 8, 10, 12], 110 | |l, r| l == r, 111 | |l, r| l < r 112 | ), 113 | vec![2, 4, 6, 8] 114 | ); 115 | assert_eq!( 116 | vec!['a', 'a', 'c', 'e'].intersect_if( 117 | vec!['A', 'B', 'C', 'E'], 118 | |l, r| l.eq_ignore_ascii_case(r), 119 | |l, r| l < &r.to_ascii_lowercase() 120 | ), 121 | vec!['a', 'c', 'e'] 122 | ); 123 | } 124 | -------------------------------------------------------------------------------- /tests/string.rs: -------------------------------------------------------------------------------- 1 | extern crate array_tool; 2 | 3 | #[test] 4 | fn it_squeezes_characters() { 5 | use array_tool::string::Squeeze; 6 | 7 | assert_eq!("yellow moon".squeeze(""), "yelow mon"); 8 | assert_eq!(" now is the".squeeze(" "), " now is the"); 9 | assert_eq!("ééé".squeeze(""), "é"); 10 | assert_eq!(" /// aa".squeeze("/"), " / aa"); 11 | 12 | let string: String = format!("yellow moon"); 13 | assert_eq!(string.squeeze(""), "yelow mon"); 14 | 15 | assert_eq!("".squeeze(""), ""); 16 | } 17 | 18 | #[test] 19 | fn it_iterates_over_every_grapheme_character() { 20 | use array_tool::string::ToGraphemeBytesIter; 21 | 22 | let temp = "a s—d féZ"; 23 | let mut giter = temp.grapheme_bytes_iter(); 24 | 25 | assert_eq!(giter.next().unwrap(), "a".as_bytes()); 26 | assert_eq!(giter.next().unwrap(), " ".as_bytes()); 27 | assert_eq!(giter.next().unwrap(), "s".as_bytes()); 28 | assert_eq!(giter.next().unwrap(), "—".as_bytes()); 29 | assert_eq!(giter.next().unwrap(), "d".as_bytes()); 30 | assert_eq!(giter.next().unwrap(), " ".as_bytes()); 31 | assert_eq!(giter.next().unwrap(), "f".as_bytes()); 32 | assert_eq!(giter.next().unwrap(), "é".as_bytes()); 33 | assert_eq!(giter.next().unwrap(), "Z".as_bytes()); 34 | assert_eq!(giter.next(), None); 35 | 36 | let somestring2 = format!("{}", "a s—d féZ"); 37 | let mut giter2 = somestring2.grapheme_bytes_iter(); 38 | 39 | assert_eq!(giter2.next().unwrap(), "a".as_bytes()); 40 | assert_eq!(giter2.next().unwrap(), " ".as_bytes()); 41 | assert_eq!(giter2.next().unwrap(), "s".as_bytes()); 42 | assert_eq!(giter2.next().unwrap(), "—".as_bytes()); 43 | assert_eq!(giter2.next().unwrap(), "d".as_bytes()); 44 | assert_eq!(giter2.next().unwrap(), " ".as_bytes()); 45 | assert_eq!(giter2.next().unwrap(), "f".as_bytes()); 46 | assert_eq!(giter2.next().unwrap(), "é".as_bytes()); 47 | assert_eq!(giter2.next().unwrap(), "Z".as_bytes()); 48 | assert_eq!(giter2.next(), None); 49 | 50 | assert_eq!("".grapheme_bytes_iter().next(), None); 51 | } 52 | 53 | #[test] 54 | fn it_justifies_one_line_in_for_string() { 55 | use array_tool::string::Justify; 56 | 57 | assert_eq!("asd asdf asd".justify_line(14), "asd asdf asd"); 58 | assert_eq!("asd asdf asd".justify_line(16), "asd asdf asd"); 59 | assert_eq!("asd as df asd".justify_line(16), "asd as df asd"); 60 | assert_eq!("asd as df asd".justify_line(18), "asd as df asd"); 61 | assert_eq!(" asd as df asd ".justify_line(16), "asd as df asd"); 62 | assert_eq!("asdasdfasd".justify_line(16), "asdasdfasd"); 63 | assert_eq!("asdasdfasd".justify_line(6), "asdasdfasd"); 64 | assert_eq!("é é".justify_line(5), "é é"); 65 | assert_eq!("a s—d féZ".justify_line(12), "a s—d féZ"); 66 | 67 | assert_eq!("".justify_line(14), ""); 68 | } 69 | 70 | #[test] 71 | fn it_substitutes_character_at_each_indexed_point() { 72 | use array_tool::string::SubstMarks; 73 | 74 | assert_eq!( 75 | "asdf asdf asdf".subst_marks(vec![0, 5, 8], "Z"), 76 | "Zsdf ZsdZ asdf" 77 | ); 78 | assert_eq!( 79 | "asdf asdf asdf".subst_marks(vec![8, 5, 0], "Z"), 80 | "Zsdf ZsdZ asdf" 81 | ); 82 | assert_eq!( 83 | "asdf asdf asdf".subst_marks(vec![0, 5, 8], "\n"), 84 | "\nsdf \nsd\n asdf" 85 | ); 86 | assert_eq!("ééé".subst_marks(vec![1], "Z"), "éZé"); 87 | assert_eq!("ééé".subst_marks(vec![1, 2], "Z"), "éZZ"); 88 | assert_eq!("ZZZ".subst_marks(vec![0, 2], "é"), "éZé"); 89 | assert_eq!("ééé".subst_marks(vec![0], "Z"), "Zéé"); 90 | 91 | assert_eq!("".subst_marks(vec![0], "Z"), ""); 92 | } 93 | 94 | #[test] 95 | fn it_seeks_end_of_whitespace_after_offset() { 96 | use array_tool::string::AfterWhitespace; 97 | 98 | assert_eq!( 99 | "asdf asdf asdf".seek_end_of_whitespace(6), 100 | Some(9) 101 | ); 102 | assert_eq!("asdf".seek_end_of_whitespace(3), Some(0)); 103 | assert_eq!("asdf ".seek_end_of_whitespace(6), None); 104 | assert_eq!("asdf".seek_end_of_whitespace(6), None); 105 | 106 | assert_eq!("".seek_end_of_whitespace(6), None); 107 | } 108 | 109 | #[test] 110 | fn it_word_wraps_for_string() { 111 | use array_tool::string::WordWrap; 112 | 113 | assert_eq!( 114 | "01234 67 9 BC EFG IJ".word_wrap(6), 115 | "01234\n67 9\nBC EFG\nIJ" 116 | ); 117 | 118 | assert_eq!("0123456789ABC EFG IJ".word_wrap(6), "0123456789ABC\nEFG IJ"); 119 | assert_eq!( 120 | "1234\n 1234 6789 1234".word_wrap(10), 121 | "1234\n 1234 6789\n1234" 122 | ); 123 | assert_eq!( 124 | "1234\n 1234 67 90 1234".word_wrap(10), 125 | "1234\n 1234 67\n90 1234" 126 | ); 127 | assert_eq!( 128 | "1234\n 1234 67 90A 1234".word_wrap(10), 129 | "1234\n 1234 67\n90A 1234" 130 | ); 131 | assert_eq!("1 \n34 ".word_wrap(3), "1 \n34 "); 132 | assert_eq!("1 34".word_wrap(3), "1 \n34"); 133 | assert_eq!("\n \n \n \n".word_wrap(1), "\n \n \n \n"); 134 | 135 | // White space to new line shouldn't add new lines. Use seek ahead. 136 | assert_eq!("\nAA\nA \nA \n".word_wrap(1), "\nAA\nA \nA \n"); 137 | assert_eq!("\n \n \n \n ".word_wrap(1), "\n \n \n \n "); 138 | } 139 | -------------------------------------------------------------------------------- /tests/vec.rs: -------------------------------------------------------------------------------- 1 | extern crate array_tool; 2 | 3 | #[test] 4 | fn it_implements_individual_uniq_on_vec() { 5 | use array_tool::vec::Uniq; 6 | assert_eq!( 7 | vec![1, 2, 3, 4, 5, 6].uniq(vec![1, 2, 5, 7, 9]), 8 | vec![3, 4, 6] 9 | ); 10 | assert_eq!( 11 | vec![ 12 | 1, 2, 3, 1, 3, 2, 1, 3, 1, 2, 3, 1, 2, 3, 3, 1, 2, 3, 3, 1, 2, 3, 1, 2, 3, 3, 4, 1, 5, 13 | 4, 6 14 | ] 15 | .uniq(vec![3, 5]), 16 | vec![1, 2, 4, 6] 17 | ); 18 | } 19 | 20 | #[test] 21 | fn it_doesnt_mutate_on_individual_uniq() { 22 | use array_tool::vec::Uniq; 23 | let a = vec![1, 2, 3, 4, 5, 6]; 24 | a.uniq(vec![1, 2, 5, 7, 9]); 25 | assert_eq!(a, vec![1, 2, 3, 4, 5, 6]); 26 | } 27 | 28 | #[test] 29 | fn it_can_return_its_own_unique() { 30 | use array_tool::vec::Uniq; 31 | assert_eq!( 32 | vec![1, 2, 1, 3, 4, 3, 4, 5, 6].unique(), 33 | vec![1, 2, 3, 4, 5, 6] 34 | ); 35 | assert_eq!( 36 | vec![ 37 | 1, 2, 3, 1, 3, 2, 1, 3, 1, 2, 3, 1, 2, 3, 3, 1, 2, 3, 3, 1, 2, 3, 1, 2, 3, 3, 4, 1, 5, 38 | 4, 6 39 | ] 40 | .unique(), 41 | vec![1, 2, 3, 4, 5, 6] 42 | ); 43 | } 44 | 45 | #[test] 46 | fn it_answers_about_uniqueness() { 47 | use array_tool::vec::Uniq; 48 | assert_eq!(vec![1, 2, 1, 3, 4, 3, 4, 5, 6].is_unique(), false); 49 | assert_eq!(vec![1, 2, 3, 4, 5, 6].is_unique(), true); 50 | } 51 | 52 | #[test] 53 | fn it_implements_individual_uniq_on_vec_via() { 54 | use array_tool::vec::Uniq; 55 | assert_eq!( 56 | vec![1.1, 2.6, 3.7, 4.7, 5.4, 6.6] 57 | .uniq_via(vec![1.5, 2.7, 5.0, 7.1, 9.4], |l: &f64, r: &f64| l.floor() 58 | == r.floor()), 59 | vec![3.7, 4.7, 6.6] 60 | ); 61 | assert_eq!( 62 | vec![ 63 | 1.2, 2.5, 3.4, 1.2, 3.8, 2.9, 1.0, 3.2, 1.2, 2.5, 3.7, 1.7, 2.9, 3.1, 3.5, 1.6, 2.7, 64 | 3.9, 3.1, 1.5, 2.6, 3.8, 1.2, 2.6, 3.7, 3.8, 4.9, 1.0, 5.1, 4.4, 6.6 65 | ] 66 | .uniq_via(vec![3.5, 5.1], |l: &f64, r: &f64| l.floor() == r.floor()), 67 | vec![1.2, 2.5, 2.9, 1.0, 1.7, 1.6, 2.7, 1.5, 2.6, 4.9, 4.4, 6.6] 68 | ); 69 | } 70 | 71 | #[test] 72 | fn it_can_return_its_own_unique_via() { 73 | use array_tool::vec::Uniq; 74 | assert_eq!( 75 | vec![1.2, 2.5, 1.4, 3.2, 4.8, 3.9, 4.0, 5.2, 6.2] 76 | .unique_via(|l: &f64, r: &f64| l.floor() == r.floor()), 77 | vec![1.2, 2.5, 3.2, 4.8, 5.2, 6.2] 78 | ); 79 | assert_eq!( 80 | vec![ 81 | 1.2, 2.5, 3.4, 1.2, 3.8, 2.9, 1.0, 3.2, 1.2, 2.5, 3.7, 1.7, 2.9, 3.1, 3.5, 1.6, 2.7, 82 | 3.9, 3.1, 1.5, 2.6, 3.8, 1.2, 2.6, 3.7, 3.8, 4.9, 1.0, 5.1, 4.4, 6.6 83 | ] 84 | .unique_via(|l: &f64, r: &f64| l.floor() == r.floor()), 85 | vec![1.2, 2.5, 3.4, 4.9, 5.1, 6.6] 86 | ); 87 | } 88 | 89 | #[test] 90 | fn it_answers_about_uniqueness_via() { 91 | use array_tool::vec::Uniq; 92 | assert_eq!( 93 | vec![1.2, 2.4, 1.5, 3.6, 4.1, 3.5, 4.7, 5.9, 6.5] 94 | .is_unique_via(|l: &f64, r: &f64| l.floor() == r.floor()), 95 | false 96 | ); 97 | assert_eq!( 98 | vec![1.2, 2.4, 3.5, 4.6, 5.1, 6.5].is_unique_via(|l: &f64, r: &f64| l.floor() == r.floor()), 99 | true 100 | ); 101 | } 102 | 103 | #[test] 104 | fn it_shifts() { 105 | use array_tool::vec::Shift; 106 | let mut x = vec![1, 2, 3]; 107 | x.unshift(0); 108 | assert_eq!(x, vec![0, 1, 2, 3]); 109 | assert_eq!(x.shift(), Some(0)); 110 | assert_eq!(x, vec![1, 2, 3]); 111 | } 112 | 113 | #[test] 114 | fn it_handles_empty_shift() { 115 | use array_tool::vec::Shift; 116 | let mut x: Vec = vec![]; 117 | assert_eq!(x.shift(), None); 118 | } 119 | 120 | #[test] 121 | fn it_intersects() { 122 | use array_tool::vec::Intersect; 123 | assert_eq!(vec![1, 1, 3, 5].intersect(vec![1, 2, 3]), vec![1, 3]) 124 | } 125 | 126 | #[test] 127 | fn it_intersects_if() { 128 | use array_tool::vec::Intersect; 129 | assert_eq!( 130 | vec!['a', 'a', 'c', 'e'] 131 | .intersect_if(vec!['A', 'B', 'C'], |l, r| l.eq_ignore_ascii_case(r)), 132 | vec!['a', 'c'] 133 | ); 134 | } 135 | 136 | #[test] 137 | fn it_multiplies() { 138 | use array_tool::vec::Times; 139 | assert_eq!(vec![1, 2, 3].times(3), vec![1, 2, 3, 1, 2, 3, 1, 2, 3]); 140 | 141 | // Empty collection 142 | let vec1: Vec = Vec::new(); 143 | let vec2: Vec = Vec::new(); 144 | assert_eq!(vec1.times(4), vec2) 145 | } 146 | 147 | #[test] 148 | fn it_joins() { 149 | use array_tool::vec::Join; 150 | assert_eq!(vec![1, 2, 3].join(","), "1,2,3") 151 | } 152 | 153 | #[test] 154 | fn it_creates_union() { 155 | use array_tool::vec::Union; 156 | assert_eq!( 157 | vec!["a", "b", "c"].union(vec!["c", "d", "a"]), 158 | vec!["a", "b", "c", "d"] 159 | ); 160 | assert_eq!( 161 | vec![ 162 | 1, 2, 3, 1, 3, 2, 1, 3, 1, 2, 3, 1, 2, 3, 3, 1, 2, 3, 3, 1, 2, 3, 1, 2, 3, 3, 4, 1, 4, 163 | 6 164 | ] 165 | .union(vec![3, 5, 7, 8, 0]), 166 | vec![1, 2, 3, 4, 6, 5, 7, 8, 0] 167 | ); 168 | } 169 | --------------------------------------------------------------------------------