Not getting max element of array - arrays

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

Removing elements from array during enumeration in Swift 5.1 without reversing [duplicate]

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 // []

Averaging elements in array of arrays by index using functional programming

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]
}

swift array storing data

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.

Easy way to check an array for contiguous three or more numbers in Swift

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

How to convert a String (numeric) in a Int array in Swift

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)

Resources