When i am parsing through the string from the url, I append each new line to an array. However I only want to add if the field is not empty. So if the column[5] is an empty string I don't append it to the array. For example I have two lines of strings:
1,2,3,4,5,
1,2,3,4,5,6
I only want to append when there are 6
However I am getting a index out of range error on the if column[5] == "" line
func readDataFromURL(url: String) -> String?{
if let url = URL(string: url)
{
do {
var contents = try String(contentsOf: url)
contents = contents.replacingOccurrences(of: "\r", with: "")
csv(data: contents)
return contents
} catch {
print("File error \(url)")
return nil
}
}
return nil
}
func csv(data: String) -> [[String]] {
var result: [[String]] = []
let rows = data.components(separatedBy: "\n")
for row in rows {
let columns = row.components(separatedBy: ",")
if columns[5] == "" {
continue
} else {
result.append(columns)
}
}
return result
}
Your current code will crash if num of elements is less than 6 , Replace
if columns[5] == "" {
continue
} else {
result.append(columns)
}
with
if columns.count > 5 && columns.last != "" {
result.append(columns)
}
I am currently using 2 arrays:
let letters:[Character] =
[" ","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","!","#","#","$","%","^","&","*","(",")","1","2","3","4","5","6","7","8","9","0","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","E","S","T","U","V","W","X","Y","Z"]
let cypher:[Character] = ["o","p","q","r","a","b","c","d","e","f","g","h","i","u","v","w","x","y","z","j","k","l","m","n","s","t","$","#","#","!","&","%","^","*","("," ",")","5","7","2","9","8","0","1","3","2","4","Q","W","E","R","T","Y","U","I","O","P","A","S","D","F","G","H","J","K","L","Z","X","C","V","B","N","M"]
Both 73 characters.
I am using this line of code to encode the inserted text:
var encode:[Character:Character] = [:]
for (index, letter) in letters.enumerated() { encode[letter] = cypher[index] }
let encodeStep1 = String(insertedText.characters.map({ encode[$0] ?? $0 }))
randomNumber = Int(arc4random_uniform(9) + 1)
var encodeStep2 = cypher.rotate(shift: randomNumber)
for (index, letter) in letters.enumerated() { encode[letter] = encodeStep2[index] }
let final = String(encodeStep1.characters.map({ encode[$0] ?? $0 }))
Decode:
var decode:[Character:Character] = [:]
let step1 = cypher.rotate(shift: (randomNumber))
for (index, letter) in step1.enumerated() { decode[letter] = letters[index] }
let step1Decoded = String(insertedEncryptedText.characters.map({ decode[$0] ?? $0 }))
for (index, letter) in cypher.enumerated() { decode[letter] = letters[index] }
let step2Decoded = String(step1Decoded.characters.map({ decode[$0] ?? $0 }))
Rotate function:
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.remove(at: 0))
}
}
else if (shift < 0) {
for i in 1...abs(shift) {
array.insert(array.remove(at: array.count-1),at:0)
}
}
}
return array
}
}
For some very odd reason, number "3" is often shown as a number "9" when decoded. From what I have seen, the problem occurs at step2Decoded. I just can not figure out what I am doing wrong. This is however a part of the code, if I need to post more I can do that.
It's because you have a typo. Your cypher array has two "2"s in it, one at cypher[39] and another 6 indices further at cypher[45]. When you're decoding in the final step you're expecting decoded["2"] to map to the value "3" in the letters array, which it does, but it's getting overwritten when it hits the "2" again setting it to the value 6 indices further in the letters array which has a value of "9".
I assume you want to change that second "2" value in the letters array to "6" instead (since I noticed there was no "6" in there). That would solve your problem.
Here is my code, inside of my for loop, there is a while loop changes
the variable i's value. I printed out the value of i before while loop
and and after loop, the new "before index value" is expected to start
at "after index value +1". However, the "new before index value" just
increase as normal, which increase one each time. Appreciate your time.
class Solution
{
func summaryRange(nums: [Int]) -> [String]
{
var result = [String]()
if nums.count == 1
{
result.append(String(nums[0])+"")
return result
}
for var i in (0...nums.count-1)
{
let a = nums[i]
print("before: \(i)")
while i+1<nums.count && (nums[i+1] - nums[i] == 1)
{
i += 1
}
if a != nums[i]
{
result.append(String(a) + "->" + String(nums[i]))
}
else
{
result.append(String(a)+"")
}
print("after: \(i)")
}
return result
}
}
var test = Solution()
var input = [0,1,2,4,5,7]
var result = test.summaryRange(input)
print(result)
This code tries to get result like ["0->2","4->5","7"]
This is my own solution. I figure out the problem. Basically, I switch
the "for loop" to a "while loop". Then, it works.
class Solution
{
func summaryRanges(nums: [Int]) -> [String]
{
var result = [String]()
var i = 0
while(i<nums.count)
{
let a = nums[i]
while i+1<nums.count && (nums[i+1] - nums[i] == 1)
{
i += 1
}
if a != nums[i]
{
result.append(String(a) + "->" + String(nums[i]))
}
else
{
result.append(String(a)+"")
}
i += 1
}
return result
}
}
In swift, for in loop is a wrapper around the Sequence Type. In this particular case, 0...numCount-1 is a Sequence of Ints. When you call for in this is what happens internally.
let a = 0..<5
var generator = a.generate()
while var i = generator.next() {
print(i + 1)
i+= 1
}
So even if you increment the value of i inside the loop, it wont affect the loop itself. This is because loop is controller only by calling genertator.next().
This helps understand the basics.
This was an interesting one. I think this does the trick (with some code cleanup as well :-]
class Solution
{
func summaryRange(nums: [Int]) -> [String] {
var result = [String]()
var currentRange = [Int]()
for num in nums {
if currentRange.isEmpty { //if range is empty, start a new range
currentRange.append(num)
continue
}
if num - 1 != currentRange.last! { //if not next number in sequence, close range, start new
result.append(closeRange(currentRange))
currentRange = []
currentRange.append(num)
} else if num - 1 == currentRange.last {
currentRange.append(num) //if next number, add to range
}
}
if !currentRange.isEmpty {
result.append(closeRange(currentRange)) //check for unclosed ranges
}
return result
}
func closeRange(range: [Int]) -> String {
guard range.count > 0 else { return "" }
if range.count == 1 { return String(range.first!) }
return "\(range.first!)->\(range.last!)"
}
}
var test = Solution()
var input = [0,1,2,4,5,7]
var result = test.summaryRange(input)
print(result)
Let me know how this works for you!
I'm trying to figure out how to loop through this usernames array, but am not sure the syntax. I am using swift in xcode
I currently have:
// Read text file
if let filepath = NSBundle.mainBundle().pathForResource("barbers", ofType: "txt")
{
do
{
//let contents = try NSString(contentsOfFile: filepath, usedEncoding: nil) as String;
//print(contents);
let text = try NSString(contentsOfFile: filepath, usedEncoding: nil) as String;
// Create character array & parse
let usernames = text.characters
.split { $0 == "\n" }
.map { String($0) }
.map { String($0.characters.split(" ")[0]) }
// Print Users
print(usernames);
// Test
}
catch
{
// Catch error?
}
}
else
{
// Print error to console log
print("Error: Could not find text file - something went wrong");
}
My question is: How can I loop through my usernames array?
I just dont know the swift syntax
I'm looking for something like
for(int i =0; i < usernames.size(); i ++ )
{
if(usernames[i] == USER)
{
b = true;
break;
}
b = false;
}
UPDATES:
Ok so I've figured out how to loop through but now I'm having troubles
I made a global variable
var abc = "";
I then did
let abc = usernames;
now when I try to do this
// Test
for i in abc.characters
{
print("abc array contents: " + i);
if(i == theUser)
{
print("Barber");
barb = true;
break;
}
print("USER " + theUser);
barb = false;
print("\n");
}
I get the error
Binary operator '+' cannot be applied to operands of type 'String' and 'Character'
and
Binary operator '==' cannot be applied to operands of type 'Character' and 'String'
All of these above work, but my favorite Swifty for loop is
for index in 0..< usernames.count {
//your code here
//to get an object at a specific index you call usernames[index]
}
In response to your updated question I believe you need to cast your character as a string. Something like
let myCharacterAsAString = String(i)
print("abc array contents: " + myCharacterAsAString);
if(myCharacterAsAString == theUser)
{
print("Barber");
barb = true;
break;
}
print("USER " + theUser);
barb = false;
print("\n");
}
for username in usernames
{
if(username == USER)
{
b = true;
break;
}
b = false;
}
or if you need access to current item's index:
for (index, username) in usernames.enumerate()
{
// same body as above
}
I believe latest versions of Swift have done away with the C-style for-loop.
This might not be super helpful but the way to iterate in Swift would likely be with a for-in loop
for username in usernames
{
if username == USER
{
b = true
break
}
b = false
}
This question already has an answer here:
Swift: sorting of array is not done correctly
(1 answer)
Closed 7 years ago.
I have an Array containing values like 7-4.json, 87-1.json and 102-4.json and want to sort it (ascending). I used the following code:
var fileNames = ["7-4.json", "87-1.json", "102-4.json"]
fileNames = fileNames.sort{ $0 < $1 }
print(fileNames)
which results in:
["102-4.json", "7-4.json", "87-1.json"]
So it didn't worked as I aspected. How can I sort it like 7-4, 87-1, 102-4?
Here you go:
var fileNames = ["7-4.json", "87-1.json", "102-4.json"]
func sortWithCustomFormat(first: String, second: String) -> Bool{
func extract(value: String) -> (Int, Int){
return (Int(value.componentsSeparatedByString("-").first!)!, Int(value.componentsSeparatedByString("-").last!.componentsSeparatedByString(".").first!)!)
}
let firstNumber = extract(first)
let secondNumber = extract(second)
if firstNumber.0 != secondNumber.0 { return firstNumber.0 < secondNumber.0 }
return firstNumber.1 < secondNumber.1
}
fileNames.sort(sortWithCustomFormat)
The function sortWithCustomFormat has a function extract that takes the inputted string and extracts the first and second numbers from it. Then, you compare the first numbers. If they are equal, then you compare the second numbers.
var fileNames = ["87-1.json", "7-4.json", "87-3.json", "102-4.json"]
fileNames = fileNames.sort({ (s1, s2) -> Bool in
let f1 = s1.stringByReplacingOccurrencesOfString(".json", withString: "")
let f2 = s2.stringByReplacingOccurrencesOfString(".json", withString: "")
let arr1 = f1.componentsSeparatedByString("-")
let arr2 = f2.componentsSeparatedByString("-")
var int1 = Int(arr1[0])
var int2 = Int(arr2[0])
if int1 < int2 {
return true
}
else if int1 > int2 {
return false
}
else {
int1 = Int(arr1[1])
int2 = Int(arr2[1])
if int1 < int2 {
return true
}
else if int1 > int2 {
return false
}
else {
return true
}
}
});
print(fileNames)
Try this...
var fileNames = ["87-1.json", "7-4.json", "102-4.json"]
// Modded OP's order to actually test sort
var sorted = fileNames.sort{ $0 < $1 }
print(sorted) // ["102-4.json", "7-4.json", "87-1.json"]
// Not sorted as OP "required", as they are string sorted, not number sorted
// Very simplistic solution
sorted = fileNames.sort { ($0 as NSString).integerValue < ($1 as NSString).integerValue}
print(sorted) // As OP requires, but...
// It won't sort on his count field - add a failing case...
fileNames = ["7-4.json", "87-1.json", "102-4.json", "102-1.json"]
sorted = fileNames.sort { ($0 as NSString).integerValue < ($1 as NSString).integerValue}
print(sorted) // ["7-4.json", "87-1.json", "102-4.json", "102-1.son"]
// WRONG!
// Define a simple function that parses his strings into tuples.
// This assumes that the Strings are valid, and fails safe if not.
// If you want more validation, add it yourself!
func myParse(s: String) -> (Int, Int) {
let c = s.componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "-."))
switch c.count {
case 0:
print("Careful Will Robinson!")
return (0, 0)
case 1:
print("Careful Will Robinson!")
return ((c[0] as NSString).integerValue, 0)
default:
return ((c[0] as NSString).integerValue, (c[1] as NSString).integerValue)
}
}
let test = fileNames.map { myParse($0) }
print("\(test)") // Test execution of function
sorted = fileNames.sort { (s1: String, s2: String) -> Bool in
let t1 = myParse(s1)
let t2 = myParse(s2)
if t1.0 == t2.0 {
return t1.1 < t2.1
} else {
return t1.0 < t2.0
}
}
print(sorted) // As required ["7-4.json", "87-1.json", "102-1.json", "102-4.json"]