I have a string array called prodIDArr having values like so...
["1","2","3","4","5","6","7","8","9","10"]
But when I do something like so..
let maxVal = prodIDArr.max()
I get the value of maxVal as 9 and not 10.
What could be the reason for this...?
That is an array of strings, and those are compared lexicographically:
"1" < "10" < "2" < ... < "9"
For example "10" < "2" because the initial characters already
satisfy "1" < "2". (For the gory details, see for example
What does it mean that string and character comparisons in Swift are not locale-sensitive?.)
Using an array of integers would be the best solution:
let prodIDArr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let maxId = prodIDArr.max()
print(maxId) // Optional(10)
If that is not possible then you can enforce a numeric comparison with
let prodIDArr = ["1","2","3","4","5","6","7","8","9","10"]
let maxId = prodIDArr.max(by: { $0.compare($1, options: .numeric) == .orderedAscending })
print(maxId) // Optional("10")
You can also map your original String Array to an Int Array
let prodIDArr = ["1","2","3","4","5","6","7","8","9","10"]
let prodNumArray = prodIDArr.map { input in Int(input)!} // or { Int($0)! }
print(prodNumArray.max()!)
Related
This question already has answers here:
Swift: Better way to remove a specific Object from an array?
(5 answers)
Closed 2 years ago.
I want to remove an element within a for-in loop if it fails a certain condition. Currently, there's a solution here, but it's old and seems complicated for all I'm trying to do.
In Java, I can just reference the index like so:
/* Program that removes negative numbers from an array */
//Make an array list and add some numbers to it.
ArrayList<Int> a = new ArrayList<Int>;
for (int i = 0; i < 10; i++){
a.add( (int) ((Math.random - 0.5) * 100) //Add 10 numbers that are pos/neg.
//Remove all the negative numbers.
for (int i = 0; i < a.size; i++){
if (a.get(i) < 0){
a.remove(i); //Removes the element.
}
}
But in Swift, to my understanding, I have to use a for-each loop equivalent:
var array = [-4, -2, 0, 4, 6, 7, 2, 5, 7]
//Iterate looking for numbers < 0.
for item in array{
if (item < 0){
array.removeAt(item) //ERROR
}
else{
//Right now, I just copy every value into a new array.
//But I shouldn't even need this w/ the Arrays collection.
}
How can I do this more simply? Sorry I'm a newbie here; thanks for your help!
Edit | Clarification June 14, 2020 at 11:44 PM:
Someone suggested filter which is a stellar response, but not what I was looking for. (My bad, sorry). I used this as an example for a different challenge I'm working on here while I learn Swift.
Please help me look for solutions that remove an element. Thank you!!
Use the removeAll(where:) method like this:
array.removeAll(where: { $0 < 0 })
or:
array.removeAll { $0 < 0 }
Expanding on the possible duplicate question link I have commented above, you can extend SignedInteger protocol and create a property to return a Boolean value based on a condition. Then you can use it as a keyPath on RangeReplaceableCollection method that removes all the elements that satisfy the given predicate.
extension SignedInteger {
var isNegative: Bool { self < 0 }
var isPositive: Bool { self > 0 }
}
extension Numeric {
var isZero: Bool { self == .zero }
}
var array = [-4, -2, 0, 4, 6, 7, 2, 5, 7]
array.removeAll(where: \.isNegative)
array // [0, 4, 6, 7, 2, 5, 7]
array.removeAll(where: \.isZero)
array // [4, 6, 7, 2, 5, 7]
array.removeAll(where: \.isPositive)
array // []
I have an array of arrays of Doubles. For example:
let mceGain = [[3,4,5],[7,4,3],[12,10,7]] // Written as integers for simplicity here
I would now like to average the elements in the different arrays with corresponding indexes. So I would have an output looking somewhat like this:
//firstAvg: (3+7+12)/3 = 7.33
//secondAvg: (4+4+10)/3 = 6
//thirdAvg: (5+3+7)/3 = 5
Then finally I would like to store these averages in a simpler array:
//mceGain: [7.33,6,5]
I have tried to do this with a double for-loop with a switch-statement inside, but this seems to be unnecessarily complicated. I assume the same result could be achieved using a combination of reduce(), map() and filter(), but I cannot seem to wrap my head around it.
Let's analyse what you want to do here. You start with an array of arrays:
[[3,4,5],[7,4,3],[12,10,7]]
and you want to transform each subarray into a number:
[7,6,5]
Whenever you have this kind of "transform each element of this sequence into something else" situation, use map.
When you compute the average, you need to transform a sequence of things into just one thing. This means that we need reduce.
let array: [[Double]] = [[3,4,5],[7,4,3],[12,10,7]]
let result = array.map { $0.reduce(0.0, { $0 + $1 }) / Double($0.count) }
With comments:
let array: [[Double]] = [[3,4,5],[7,4,3],[12,10,7]]
let result = array.map { // transform each element like this:
$0.reduce(0.0, { $0 + $1 }) // sums everything in the sub array up
/ Double($0.count) } // divide by count
EDIT:
What you need to do is to "transpose" the array first, then do the map and reduce:
array[0].indices.map{ index in // these three lines makes the array [[3, 7, 12], [4, 4, 10], [5, 3, 7]]
array.map{ $0[index] }
}
.map { $0.reduce(0.0, { $0 + $1 }) / Double($0.count) }
This should answer your comment below
let elms: [[Double]] = [[3, 5, 3], [4, 4, 10] , [5, 3, 7]]
func averageByIndex(elms:[[Double]]) -> [Double]? {
guard let length = elms.first?.count else { return []}
// check all the elements have the same length, otherwise returns nil
guard !elms.contains(where:{ $0.count != length }) else { return nil }
return (0..<length).map { index in
let sum = elms.map { $0[index] }.reduce(0, +)
return sum / Double(elms.count)
}
}
if let averages = averageByIndex(elms: elms) {
print(averages) // [4.0, 4.0, 6.666666666666667]
}
I made a function that returns multiple values
let interestingNumbers = [ // String:Array<Int>
"Prime": [2, 3, 5, 7, 11, 23],
"Fibonacci": [1, 1, 2, 3, 5, 80],
"Square": [1, 4, 9, 16, 25],
]
func largestNum(objDictionary:[String:Array<Int>]) -> (Int,String) {
var largest = 0
var ki:String? = nil
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
ki=kind
}
}
}
return (largest , ki!)
}
print(largestNum(interestingNumbers)) //calling fuction and print
/*var ar2:[Int,String] = largestNum(interestingNumbers))
print(ar2)*/' this code have an error`
How can I store the returned values from the function in the array
Update:
If you want both values in a single array with ar[0] being the Int and ar[1] being the String, then you'll need to declare ar2 to be [Any] and unpack the tuple when initializing ar2:
let largest = largestNum(interestingNumbers)
var ar2:[Any] = [largest.0, largest.1]
print(ar2) // [80, "Fibonacci"]
If you just assign the return to ar2 and leave it as a tuple, you can access the values with ar2.0 and ar2.1:
var ar2 = largestNum(interestingNumbers)
print(ar2.0) // 80
print(ar2.1) // "Fibonacci"
Or if you change your largestNum to return a named tuple:
func largestNum(objDictionary:[String:Array<Int>]) -> (number: Int, kind: String) {
}
var ar2 = largestNum(interestingNumbers)
print(ar2.number) // 80
print(ar2.kind) // "Fibonacci"
Original Answer:
Declare your array ar2 to hold tuples pairs of Int and String, and then wrap your return value in [] to create an array:
var ar2:[(Int,String)] = [largestNum(interestingNumbers)]
print(ar2) // [(80, "Fibonacci")]
Because tuples are really meant for temporary values, it is better style to store values in an array using a struct:
Change your function to return an InterestingNumber:
struct InterestingNumber {
let kind: String
let number: Int
}
func largestNum(objDictionary:[String:Array<Int>]) -> InterestingNumber {
// contents omitted for brevity
return InterestingNumber(kind: ki!, number: largest)
}
let largest = largestNum(interestingNumbers)
// Define your array to hold `InterestingNumber`s:
var ar2:[InterestingNumber] = [largest]
print(ar2) // [InterestingNumber(kind: "Fibonacci", number: 80)]
If you meant for ar2 to just hold a single value, then simply do:
var ar2 = largestNum(interestingNumbers)
and Swift will infer the type (which is a tuple in your original code or an InterestingNumber when using the struct.
your code runs fine in xcode 7.3.1 playground
okay, now i get your question:
let z: (Int, String) = largestNum(interestingNumbers)
The part after the arrow in your function definition is the type (i think called tupel), you can use it for a variable.
Is there any easy way to check if an array contains contiguous value of three or more? e.g. [4, 2, 1, 1, 1, 7, 4, 4, 4, 4] contains two contiguous sequence of 1 and 4. To check I wish to give 1 and minimum allowed conjugation, in this case 2, it will just return true. Thanks.
We can start out by making use of a neat extension to SequenceType by user #oisdk in his answer in the following thread:
How to find same value(duplicate) in an int array in order?
The extension groups successive elements in tuples (value, numberOfSuccessions):
/* from SO user #oisdk:s answer in Q&A:
https://stackoverflow.com/a/35325141/4573247 */
extension SequenceType where Generator.Element: Equatable {
func group() -> [(Generator.Element, Int)] {
var res: [(Generator.Element, Int)] = []
for el in self {
if res.last?.0 == el {
res[res.endIndex-1].1 += 1
} else {
res.append((el,1))
}
}
return res
}
}
Using this, we can swiftly write another extension for checking if---for a given array---a contiguous sequence (for some minimum number of successions/repeats) exists for a given number:
extension SequenceType where Generator.Element == Int {
func containsContiguousValue(value: Int, forMinimumRepeats rep: Int) -> Bool {
return !self
.group()
.contains{ (val, count) in count >= rep && val == value }
}
}
Used as follows
/* Example usage */
let array = [4, 2, 1, 1, 1, 7, 4, 4, 4, 4]
array.containsContiguousValue(1, forMinimumRepeats: 3) // true
array.containsContiguousValue(1, forMinimumRepeats: 4) // false
array.containsContiguousValue(4, forMinimumRepeats: 4) // true
array.containsContiguousValue(2, forMinimumRepeats: 3) // false
I think the simplest possible way is with the help of the reduce function. If you want you can extend the data structures, but I am not quite a fan of that. So here is a simple solution to your example
// example array
let a = [4, 2, 1, 1, 1, 7, 4, 4, 4, 4]
let minRepeats = 3 // desired min repeats
let elementToCheck = 4 // element to check
let m = a.reduce(0) { (initial: Int, el: Int) -> Int in
if initial >= minRepeats {
return initial
} else {
return el == elementToCheck ? initial + 1 : 0
}
}
// if m == minRepeats the check is positive, if m < minRepeats the check is negative
// let check = a.reduce(0){...} == minRepeats gives you the right result
// Thanks to user3441734 for the correction
The answers above were helpful but not quite as generic as I needed. Also, they are a little outdated, so for those who come across this requirement, here's a generic reusable Swift 4.2 implementation:
An extension on any Collection that returns an array of ranges representing the indices of consecutive elements in a collection matching a given predicate.
https://gist.github.com/shaps80/8ec24f82ad1e54d42709277ec2af93a3
I'd like to know how can I convert a String in an Int array in Swift.
In Java I've always done it like this:
String myString = "123456789";
int[] myArray = new int[myString.lenght()];
for(int i=0;i<myArray.lenght;i++){
myArray[i] = Integer.parseInt(myString.charAt(i));
}
Thanks everyone for helping!
let str = "123456789"
let intArray = map(str) { String($0).toInt() ?? 0 }
map() iterates Characters in str
String($0) converts Character to String
.toInt() converts String to Int. If failed(??), use 0.
If you prefer for loop, try:
let str = "123456789"
var intArray: [Int] = []
for chr in str {
intArray.append(String(chr).toInt() ?? 0)
}
OR, if you want to iterate indices of the String:
let str = "123456789"
var intArray: [Int] = []
for i in indices(str) {
intArray.append(String(str[i]).toInt() ?? 0)
}
You can use flatMap to convert the characters into a string and coerce the character strings into an integer:
Swift 2 or 3
let string = "123456789"
let digits = string.characters.flatMap{Int(String($0))}
print(digits) // [1, 2, 3, 4, 5, 6, 7, 8, 9]"
Swift 4
let string = "123456789"
let digits = string.flatMap{Int(String($0))}
print(digits) // [1, 2, 3, 4, 5, 6, 7, 8, 9]"
Swift 4.1
let digits = string.compactMap{Int(String($0))}
Swift 5 or later
We can use the new Character Property wholeNumberValue https://developer.apple.com/documentation/swift/character/3127025-wholenumbervalue
let digits = string.compactMap{$0.wholeNumberValue}
#rintaro's answer is correct, but I just wanted to add that you can use reduce to weed out any characters that can't be converted to an Int, and even display a warning message if that happens:
let str = "123456789"
let intArray = reduce(str, [Int]()) { (var array: [Int], char: Character) -> [Int] in
if let i = String(char).toInt() {
array.append(i)
} else {
println("Warning: could not convert character \(char) to an integer")
}
return array
}
The advantages are:
if intArray contains zeros you will know that there was a 0 in str, and not some other character that turned into a zero
you will get told if there is a non-Int character that is possibly screwing things up.
Swift 3
Int array to String
let arjun = [1,32,45,5]
print(self.get_numbers(array: arjun))
func get_numbers(array:[Int]) -> String {
let stringArray = array.flatMap { String(describing: $0) }
return stringArray.joined(separator: ",")
String to Int Array
let arjun = "1,32,45,5"
print(self.get_numbers(stringtext: arjun))
func get_numbers(stringtext:String) -> [Int] {
let StringRecordedArr = stringtext.components(separatedBy: ",")
return StringRecordedArr.map { Int($0)!}
}
var myString = "123456789"
var myArray:[Int] = []
for index in 0..<countElements(myString) {
var myChar = myString[advance(myString.startIndex, index)]
myArray.append(String(myChar).toInt()!)
}
println(myArray) // [1, 2, 3, 4, 5, 6, 7, 8, 9]"
To get the iterator pointing to a char from the string you can use advance
The method to convert string to int in Swift is toInt()
Swift 3 update:
#appzYourLife : That's correct toInt() method is no longer available for String in Swift 3.
As an alternative what you can do is :
intArray.append(Int(String(chr)) ?? 0)
Enclosing it within Int() converts it to Int.
Swift 3: Functional Approach
Split the String into separate String instances using:
components(separatedBy separator: String) -> [String]
Reference: Returns an array containing substrings from the String that have been divided by a given separator.
Use the flatMap Array method to bypass the nil coalescing while converting to Int
Reference: Returns an array containing the non-nil results of calling the given transformation with each element of this sequence.
Implementation
let string = "123456789"
let intArray = string.components(separatedBy: "").flatMap { Int($0) }
let array = "0123456789".compactMap{ Int(String($0)) }
print(array)