I have an array of tuples var label = [(x: CGFloat, y: CGFloat, v1: String, v2: String)]() and would like to find the string of longest length in v1.
I could iterate through the array like this:
var maxlength = 0
var position = 0
for i in 0 ..< label.count {
if label[i].v1.count > maxlength {
maxlength = label[i].v1.count
position = i
}
}
print("longest string: \(maxlength) chars, at position \(position), is \(label[position].v1)")
but is there a more concise method?
You can use the max(by:) operator on your label array.
let longestString = label.max { $0.v1 < $1.v1}
Or a combination of map and max, as you're only interested in the longest string, not the tuple with the longest string value.
let longestString = label.map(\.v1).max()
For finding the position you have 2 options
enumerated:
let positionOfLongestString: Int? = label
.map(\.v1)
.enumerated() // converts array to a (offset: Index, element: String) tuple array
.max { $0.element < $1.element }?
.offset
firstIndexOf:
let positionOfLongestString: Int? = label
.map(\.v1)
.max()
.flatMap { longestString in
label.firstIndex { $0.v1 == longestString }
}
Related
I'm getting an Xcode error
Global function 'zip' requires that 'String.Element' (aka 'Character') conform to 'Sequence'
on array[i] = zip(array[i], arrayOfZeros)
func expandedForm(_ num: Int) -> String {
let lenght = String(num).count
var array = Array(String(num))
var i = 0
while array.count > i {
let numberOfZeros = array.count - 1 - i
let arrayOfZeros = Array(repeating: "0", count: numberOfZeros)
let string = array[i]
array[i] = zip(array[i], arrayOfZeros)
i += 1
}
return ""
}
I'm trying to merge two array of strings, but I think they are not the same type. Need some help on this.
func expandedForm(_ num: Int) -> String {
let lenght = String(num).count
var array = Array(String(num))
var i = 0
while array.count > i {
let numberOfZeros = array.count - 1 - i
let arrayOfZeros = Array(repeating: "0", count: numberOfZeros)
let string = array[i]
array[i] = zip(array[i], arrayOfZeros)
i += 1
}
return ""
}
You have some issues.
var array = Array(String(num)), if you pass your cursor on array, it's a [String.Element], ie a [Character], an array a Character.
So let string = array[i], that's misleading, because array[i] is a Character, not a String.
zip(_:_:) awaits for two sequences (~array), and you give as first parameter a Character.
Possible solutions:
Make array, a real array of String:
var array = Array(String(num)).map { String($0) }
And then:
array[i] = array[i] + arrayOfZeros.joined()
Or create a new variable var output that will be an array of String:
var output: [String] = []
And populate it:
output.append(String(array[i] + arrayOfZeros.joined())
I am new to swift & I have to remove characters from odd index from a given String
I did try with following code
var myStringObject = "HelloTestString"
myStringObject.enumerated().filter({ index, char in !(char == "1" && index % 2 == 0) })
but I am unable to find the desired result string. Can you please guide me how to remove characters from odd index in String.
You can't filter by two filter types. So you have to move to the old approach.
var myStringObject = "HelloTestString"
var newString = ""
var index = 0
while index < myStringObject.count {
if index % 2 != 0 {
let firstIndex: String.Index = myStringObject.startIndex
let desiredChar: Character = myStringObject[myStringObject.index(firstIndex, offsetBy: index)]
newString = newString + "\(desiredChar)"
}
index = index + 1
}
print(newString) //elTsSrn
Keeping your starting solution:
let couples = myStringObject.enumerated().filter { (arg0) -> Bool in
let (offset, _) = arg0
return offset % 2 == 0
}
print(couples)
or
let couples = myStringObject.enumerated().filter { (offset, _) -> Bool in
return offset % 2 == 0
} //Which is more similar to last version
You have then, an array of Tuples where first element is the offset, and the second the element.
$>[(offset: 0, element: "H"), (offset: 2, element: "l"), (offset: 4, element: "o"), (offset: 6, element: "e"), (offset: 8, element: "t"), (offset: 10, element: "t"), (offset: 12, element: "i"), (offset: 14, element: "g")]
Let's keep only the letters (and back to String, not a String.Element):
let onlyletters = couples.map({ String($0.element) })
Let's get it back into a String.
let result = onlyletters.joined()
In one line:
let oneLine = myStringObject.enumerated().filter({ $0.0 % 2 == 0 }).map({ String($0.element) }).joined()
You can create an auxiliary index and use defer to increase it after each iteration on your collection, this way you don't need to enumerate your string:
let string = "HelloTestString"
var index = 0
let filtered = string.filter { _ in
defer { index += 1 }
return index % 2 == 1
}
print(filtered) // "elTsSrn"
If you need to mutate your original string you can use removeAll with the same approach:
var string = "HelloTestString"
var index = 0
string.removeAll { _ in
defer { index += 1 }
return index % 2 == 0
}
print(string)
Implementing your own method
extending RangeReplaceableCollection to filter or removeAll elements that are in odd or even positions:
extension RangeReplaceableCollection {
var oddIndicesElements: Self {
var position = 0
return filter { _ in
defer { position += 1 }
return position % 2 == 1
}
}
var evenIndicesElements: Self {
var position = 0
return filter { _ in
defer { position += 1 }
return position % 2 == 0
}
}
mutating func removeAllEvenIndicesElements() {
var position = 0
removeAll { _ in
defer { position += 1 }
return position % 2 == 0
}
}
mutating func removeAllOddIndicesElements() {
var position = 0
removeAll { _ in
defer { position += 1 }
return position % 2 == 1
}
}
}
var myStringObject = "HelloTestString"
print(myStringObject.oddIndicesElements) // "elTsSrn"
myStringObject.removeAllEvenIndicesElements()
print(myStringObject) // "elTsSrn"
I am currently working on a simple program to gradually and randomly visualize a string in two iteration. Right now I have managed to get the first iteration but I'm not sure how to do the second one. If someone could give any example or advice I would be very grateful. My code looks like this:
let s = "Hello playground"
let factor = 0.25
let factor2 = 0.45
var n = s.filter({ $0 != " " }).count // # of non-space characters
var m = lrint(factor * Double(n)) // # of characters to display
let t = String(s.map { c -> Character in
if c == " " {
// Preserve space
return " "
} else if Int.random(in: 0..<n) < m {
// Replace
m -= 1
n -= 1
return c
} else {
// Keep
n -= 1
return "_"
}
})
print(t) // h_l__ _l_______d
To clarify, I want to use factor2 in the second iteration to print something that randomly add letters on top of t that looks something like this h_l_o pl_g_____d.
Replacing Characters
Starting from #MartinR's code, you should remember the indices that have been replaced. So, I am going to slightly change the code that replaces characters :
let s = "Hello playground"
let factor = 0.25
let factor2 = 0.45
var n = s.filter({ $0 != " " }).count // # of non-space characters
let nonSpaces = n
var m = lrint(factor * Double(n)) // # of characters to display
var indices = Array(s.indices)
var t = ""
for i in s.indices {
let c = s[i]
if c == " " {
// Preserve space
t.append(" ")
indices.removeAll(where: { $0 == i })
} else if Int.random(in: 0..<n) < m {
// Keep
m -= 1
n -= 1
t.append(c)
indices.removeAll(where: { $0 == i })
} else {
// Replace
n -= 1
t.append("_")
}
}
print(t) //For example: _e___ ______ou_d
Revealing Characters
In order to do that, we should calculate the number of characters that we want to reveal:
m = lrint((factor2 - factor) * Double(nonSpaces))
To pick three indices to reveal randomly, we shuffle indices and then replace the m first indices :
indices.shuffle()
var u = t
for i in 0..<m {
let index = indices[i]
u.replaceSubrange(index..<u.index(after: index), with: String(s[index]))
}
indices.removeSubrange(0..<m)
print(u) //For example: _e__o _l__g_ou_d
I wrote StringRevealer struct, that handle all revealing logic for you:
/// Hide all unicode letter characters as `_` symbol.
struct StringRevealer {
/// We need mapping between index of string character and his position in state array.
/// This struct represent one such record
private struct Symbol: Hashable {
let index: String.Index
let position: Int
}
private let originalString: String
private var currentState: [Character]
private let charactersCount: Int
private var revealed: Int
var revealedPercent: Double {
return Double(revealed) / Double(charactersCount)
}
private var unrevealedSymbols: Set<Symbol>
init(_ text: String) {
originalString = text
var state: [Character] = []
var symbols: [Symbol] = []
var count = 0
var index = originalString.startIndex
var i = 0
while index != originalString.endIndex {
let char = originalString[index]
if CharacterSet.letters.contains(char.unicodeScalars.first!) {
state.append("_")
symbols.append(Symbol(index: index, position: i))
count += 1
} else {
state.append(char)
}
index = originalString.index(after: index)
i += 1
}
currentState = state
charactersCount = count
revealed = 0
unrevealedSymbols = Set(symbols)
}
/// Current state of text. O(n) conplexity
func text() -> String {
return currentState.reduce(into: "") { $0.append($1) }
}
/// Reveal one random symbol in string
mutating func reveal() {
guard let symbol = unrevealedSymbols.randomElement() else { return }
unrevealedSymbols.remove(symbol)
currentState[symbol.position] = originalString[symbol.index]
revealed += 1
}
/// Reveal random symbols on string until `revealedPercent` > `percent`
mutating func reveal(until percent: Double) {
guard percent <= 1 else { return }
while revealedPercent < percent {
reveal()
}
}
}
var revealer = StringRevealer("Hello товарищ! 👋")
print(revealer.text())
print(revealer.revealedPercent)
for percent in [0.25, 0.45, 0.8] {
revealer.reveal(until: percent)
print(revealer.text())
print(revealer.revealedPercent)
}
It use CharacterSet.letters inside, so most of languages should be supported, emoji ignored and not-alphabetic characters as well.
I have an array of CGPoints (which are arranged in a specific order) [A1, A2,...,AN]. How can I compare the elements in pairs of two to determine which has a greater x position? For example, comparing A1.x with A2.x, A2.x with A3.x, A3.x with A4.x and so on all the way to AN.x with A1.x (comparing elements in specific order).
I wrote extension function pairwiseCompare(index: Self.Index?) -> Bool?, that take index of element and compare this element with next (or with first, if it is last):
extension CollectionType where Generator.Element == CGPoint {
func pairwiseCompare(index: Self.Index?) -> Bool? {
guard let index = index where startIndex.distanceTo(index) >= 0 && index.distanceTo(endIndex) > 0 else {
return nil
}
let secondIndex = index.successor() == endIndex ? startIndex : index.successor()
return self[index].x > self[secondIndex].x
}
func pairwiseCompare() -> [Bool] {
var result = [Bool]()
for index in startIndex..<endIndex {
result.append(pairwiseCompare(index)!)
}
return result
}
}
Usage:
let array = [CGPoint(x: 1, y: 1), CGPoint(x: 2, y: 1), CGPoint(x: 5, y: 1)]
print(array.pairwiseCompare(-1)) // nil
print(array.pairwiseCompare(0)) // Optional(false)
print(array.pairwiseCompare(1)) // Optional(false)
print(array.pairwiseCompare(2)) // Optional(true)
print(array.pairwiseCompare(3)) // nil
print(array.pairwiseCompare()) // [false, false, true]
Array of Colors
let colorArray = [
UIColor.redColor(),
UIColor.orangeColor(),
UIColor.yellowColor(),
UIColor.greenColor(),
UIColor.blueColor()
]
The goal is to shift the array:
To start with a different color.
To preserve the circular order of colors.
Example #1
If we wanted to start with the orange color (the color at index 1 in the original array), the array would look like this:
let colorArray = [
UIColor.orangeColor(),
UIColor.yellowColor(),
UIColor.greenColor(),
UIColor.blueColor(),
UIColor.redColor(),
]
Example #2
If we wanted to start with the green color (the color at index 3 in the original array), the array would look like this:
let colorArray = [
UIColor.greenColor(),
UIColor.blueColor(),
UIColor.redColor(),
UIColor.orangeColor(),
UIColor.yellowColor()
]
Short & clear Swift 3 & 4 solution I came up with:
extension Array {
func shifted(by shiftAmount: Int) -> Array<Element> {
// 1
guard self.count > 0, (shiftAmount % self.count) != 0 else { return self }
// 2
let moduloShiftAmount = shiftAmount % self.count
let negativeShift = shiftAmount < 0
let effectiveShiftAmount = negativeShift ? moduloShiftAmount + self.count : moduloShiftAmount
// 3
let shift: (Int) -> Int = { return $0 + effectiveShiftAmount >= self.count ? $0 + effectiveShiftAmount - self.count : $0 + effectiveShiftAmount }
// 4
return self.enumerated().sorted(by: { shift($0.offset) < shift($1.offset) }).map { $0.element }
}
}
Explanation:
Arrays with no elements and shifts producing the identity of the
original array are returned immediately
To get the effective shift amount regardless of the amount passed with the function, we do some modulo calculation to get rid of shifts that would rotate the elements in the array more than once (e.g. in an Array with 5 Objects, a shift of +7 is the same as a shift of +2). Since we always want to shift to the right, in order to be done with one simple function instead of two, negative inputs have to be dealt with (e.g. in an Array with 5 Objects, a shift of -2 is the same as a shift of +3). Therefore we adjust the negative results of the modulo calculation by the length of the array.
Of course those 3 lines could be done in one, but I wanted to make this as readable as possible.
Now we prepare the actual shift by taking the index ($0) of element and returning the shifted index by adding the amount calculated in step 2. If the new index lands outside of the array length, it needs to be wrapped around to the front.
And finally we apply all the preparation to our array with some trickery: enumerated() gives us an array of tuples [(offset: Int, element: Int)], which is simply the original index of every element and the element itself. We then sort this enumerated array by the manipulated offset (aka the element's index) via applying the function from step 3. Lastly we get rid of the enumeration by mapping the sorted elements back into an array.
This extension works with arrays of any type. Examples:
let colorArray = [
UIColor.red,
UIColor.orange,
UIColor.yellow,
UIColor.green,
UIColor.blue
]
let shiftedColorArray = [
UIColor.green,
UIColor.blue,
UIColor.red,
UIColor.orange,
UIColor.yellow
]
colorArray.shifted(by: 2) == shiftedColorArray // returns true
[1,2,3,4,5,6,7].shifted(by: -23) // returns [3,4,5,6,7,1,2]
I know this might be late. But the easiest way to rotate or shift an array is
func shifter(shiftIndex: Int) {
let strArr: [String] = ["a","b","c","d"]
var newArr = strArr[shiftIndex..<strArr.count]
newArr += strArr[0..<shiftIndex]
println(newArr) }
shifter(2) //[c, d, a, b] you can modify the function to take array as input
You can extend Array to include a method to return an array containing the elements of the original array rotated by one element:
extension Array {
func rotate(shift:Int) -> Array {
var array = Array()
if (self.count > 0) {
array = self
if (shift > 0) {
for i in 1...shift {
array.append(array.removeAtIndex(0))
}
}
else if (shift < 0) {
for i in 1...abs(shift) {
array.insert(array.removeAtIndex(array.count-1),atIndex:0)
}
}
}
return array
}
}
To shifts the elements of an array once
let colorArray:[UIColor] = [
.redColor(),
.orangeColor(),
.yellowColor(),
.greenColor(),
.blueColor()
]
let z = colorArray.rotate(1)
// z is [.orangeColor(), .yellowColor(), .greenColor(), .blueColor(), .redColor()]
and twice
let z = colorArray.rotate(2)
// z is [.yellowColor(), .greenColor(), .blueColor(), .redColor(), .orangeColor()]
A variation of #zizutg's answer, that can shift in both directions (positive and negative)
extension Array {
public func shifted(by index: Int) -> Array {
let adjustedIndex = index %% self.count
return Array(self[adjustedIndex..<self.count] + self[0..<adjustedIndex])
}
}
// True modulo function https://stackoverflow.com/a/41180619/683763
infix operator %%
public func %%(_ dividend: Int, _ divisor: Int) -> Int {
precondition(divisor > 0, "modulus must be positive")
let reminder = dividend % divisor
return reminder >= 0 ? reminder : reminder + divisor
}
You can iterate by handling starting index.
func iterate<T>(array:Array<T>, start:Int, callback:(T) -> ()) {
let count = array.count
for index in start..<(start + count) {
callback(array[index % count])
}
}
If you want to start from index 3
iterate(colors, 3, { (color) -> () in println("color - \(color)")})