Swift 3: Restrict .components array and use reduce - arrays

I try to get the first n parts of a path:
var rootPath = "/p1/p2"
var filePath = "/p1/p2/p3/p4/file.ext"
I want to get one more than used by rootPath = 3 folders deep:
result = "/p1/p2/p3/"
I try to use build-in array components, but I don't know how to restrict it to a specific number:
func getRootFolderRel(rootURL : URL, fileURL : URL, relIndex : Int) -> String {
let pccount = rootURL.pathComponents.count + 1
var count = 0
return fileURL.pathComponents.reduce("", { count += 1 < pccount ? $0.stringByAppendingPathComponent(pathComponent: $1) : $0 })
}
Any hints? (It should be as fast as possible) :-)
Added: (I wrote an working function, but it is not using internal commands. Everything is done by me, so I assume, it is very slow compared to solutions based on build-in functions).
func getRootFolderRel(rootURL : URL, fileURL : URL, relIndex : Int) -> String {
let pccount = rootURL.pathComponents.count + relIndex
var array = [String]()
var count = 0
while count < pccount && count < rootURL.pathComponents.count {
array.append(rootURL.pathComponents[count])
count += 1
}
let offset = count
while count < pccount && count < fileURL.pathComponents.count {
array.append(fileURL.pathComponents[count])
count += 1
}
return array.reduce("", { $0.stringByAppendingPathComponent($1) })
}

Related

Calculate the data received inside the recursive function

The function outputs via print() all possible combinations of the characters "abc". (Depending on the specified length)
I need to calculate this amount. I only managed to output these combinations one by one through print(). I left a comment in the right place of the code.
func allLexicographicRecur (_ string: [String.Element], _ data: [String], _ last: Int, _ index: Int){
var length = string.count-1
var data = data
for i in 0...length {
data[index] = String(string[i])
if index == last {
print(data.joined()) // Displays a combination. It is necessary to somehow calculate.
}else{
allLexicographicRecur(string, data, last, index+1)
}
}
}
func allLexicographic(_ l: Int) {
var alphabet = "abc"
var data = Array(repeating: "", count: l)
var string = alphabet.sorted()
var counter = 0
allLexicographicRecur(string, data, l-1, 0)
}
allLexicographic(3)
The function must somehow return the number of these combinations.
I would be very grateful for the help!
I managed to count only this way (but most likely it is not the best way to do it):
var count = 0
func allLexicographicRecur (_ string: [String.Element], _ data: [String], _ last: Int, _ index: Int){
var length = string.count-1
var data = data
for i in 0...length {
data[index] = String(string[i])
if index == last {
print(data.joined()) // Displays a combination. It is necessary to somehow calculate.
count += 1
}else{
allLexicographicRecur(string, data, last, index+1)
}
}
}
func allLexicographic(_ l: Int) {
var alphabet = "abc"
var data = Array(repeating: "", count: l)
var string = alphabet.sorted()
var counter = 0
allLexicographicRecur(string, data, l-1, 0)
}
allLexicographic(3)
print(count)
You do not need a global variable. There are at least two other options. You can add an inout parameter to allLexicographicRecur to keep track of the count or you can have allLexicographicRecur return its count.
Here's your code using a return value:
func allLexicographicRecur(_ string: [String.Element], _ data: [String], _ last: Int, _ index: Int) -> Int {
let length = string.count - 1
var data = data
var count = 0
for i in 0...length {
data[index] = String(string[i])
if index == last {
print(data.joined()) // Displays a combination. It is necessary to somehow calculate.
count += 1
} else {
count += allLexicographicRecur(string, data, last, index + 1)
}
}
return count
}
func allLexicographic(_ l: Int) -> Int {
let alphabet = "abc"
let data = Array(repeating: "", count: l)
let string = alphabet.sorted()
return allLexicographicRecur(string, data, l - 1, 0)
}
print(allLexicographic(3))
Here's your code updated to use an inout parameter.
func allLexicographicRecur(_ string: [String.Element], _ data: [String], _ last: Int, _ index: Int, _ count: inout Int){
let length = string.count - 1
var data = data
for i in 0...length {
data[index] = String(string[i])
if index == last {
print(data.joined()) // Displays a combination. It is necessary to somehow calculate.
count += 1
} else {
allLexicographicRecur(string, data, last, index + 1, &count)
}
}
}
func allLexicographic(_ l: Int) -> Int {
let alphabet = "abc"
let data = Array(repeating: "", count: l)
let string = alphabet.sorted()
var counter = 0
allLexicographicRecur(string, data, l - 1, 0, &counter)
return counter
}
print(allLexicographic(3))
You can not mange the count without global variable because of recursive function. so the method you wrote in question is perfect as per the output you want to have.

Swift- How to add onto arraylist next index everytime save button is pressed

Hi my question is how can I store a string value which contails all textfield input into an array with new index every time I press save. I have created some code below however I think its overriding the first index.
#IBAction func Save (_ sender: UIButton){
let firstextfield = textfield1.text
let secondtextfield = textfield2.text
let allText = firsttextfield! + "," + secondtextfield!
var myArray = [String]()
var index : Int = 0
while (myArray.count) <= index {
myArray.insert(allText, at: index)
}
index +=
for element in myArray{
print(element)
}
}
input: firsttextfield = 9.22 and secondtextfield = 91.2
save button is then pressed.
output:
Optional ("9.22,91.2")
Optional ("")
if i were to then change the values of my textfields to firsttextfield = 0.2 and secondtextfield = 20.2
I get output :
Optional ("0.2,20.2")
Optional ("")
I dont want it to overide the array but to add onto it so expected output:
Optional ("9.22,91.2")
Optional ("0.2,20.2")
Any tips are welcome as I am new to coding.
//Just Declare outside of button action
var index = 0
var myArray = [String?]()
//Button action
#IBAction func btnShareTapped(_ sender: UIButton) {
let firstextfield = "textfield1.text"
let secondtextfield = "textfield2.text"
let allText = firstextfield + "," + secondtextfield
while (myArray.count) <= index {
myArray.append("")
}
myArray.insert(allText, at: index)
index = index + 1
for element in myArray{
print(element)
}
print(myArray)
}
Output
[Optional("textfield1.text,textfield2.text"), Optional("")]
[Optional("textfield1.text,textfield2.text"), Optional("textfield1.text,textfield2.text"), Optional("")]
//your last comment answer is
let a : String = myArray[0]!
let b = a.split(separator: ",")
label.text = b[0] as! String
label1.text = b[1] as! String
I think your emptying the array here:
var myArray = [String?]()
var index = 0
while (myArray.count) <= index {
myArray.append("")
}
declare and initialise it outside the function and then use this to insert values to array:
let allText = firsttextfield! + "," + secondtextfield!
anArray.append(allText)

How to improve the speed of the code?

There is a speed problem with code below if the number of elements in arrayOfA is around 1000 and in arrayOfB around 100 000 (run time is about several minutes). How to improve the speed ?
let arrayOfA = [(String, String)]() //second string in tuple is about 30 characters
let arrayOfB = [String]() //string is about 200 characters
var arrayOfC = [(String, Int)]()
for i in 0..<arrayOfA.count
{
var amount = Int()
let name = arrayOfA[i].0 + " " + arrayOfA[i].1
for j in 0..<arrayOfB.count
{
if arrayOfB[j].range(of: arrayOfA[i].1) != nil
{
amount += 1
}
}
if amount > 0
{
arrayOfC += [(name, amount)]
}
}
Update
Meanwhile, I have made another code which is faster about 4 times. The idea is to avoid unnecessary repeating steps for function .range(of: string) . So, the first step is joining of reads with interval "_" Then, I can search to find all Range<String.Index> for stringA in the stringOfarrayOfB by using extension method searchForStringInString (which can find All Range<String.Index> of stringIn in stringOut).
let stringOfarrayOfB = arrayOfB.joined(separator: "_")
for i in 0..<arrayOfA.count
{
var amount = Int()
let arrayOfRanges = stringOfarrayOfB.searchForStringInString(stringOut: stringOfarrayOfB, stringIn: arrayOfA[i].1)
amount = arrayOfRanges.count
if amount > 0
{
let name = arrayOfA[i].0 + " " + arrayOfA[i].1
arrayOfC += [(name, amount)]
}
}
This is the code after it was changed with the suggested comments.
let arrayOfA = [(String, String)]() //second string in tuple is about 30 characters
let arrayOfB = [String]() //string is about 200 characters
var arrayOfC = [(String, Int)]()
for i in 0..<arrayOfA.count
{
var amount = Int()
for (index, stringB) in arrayOfB.enumerate().reverse()
{
if stringB.range(of: arrayOfA[i].1) != nil
{
amount += 1
arrayOfB.removeAtIndex(index)
}
}
if amount > 0
{
let name = arrayOfA[i].0 + " " + arrayOfA[i].1
arrayOfC += [(name, amount)]
}
}
Update
Same functionallity without reversing the array
let arrayOfA = [(String, String)]() //second string in tuple is about 30 characters
let arrayOfB = [String]() //string is about 200 characters
var arrayOfC = [(String, Int)]()
for i in 0..<arrayOfA.count
{
var amount = Int()
var indexesToDelete = []
for (index, stringB) in arrayOfB.enumerate()
{
if stringB.range(of: arrayOfA[i].1) != nil
{
amount += 1
indexesToDelete.append(index)
}
}
for (index, indexToDelete) in indexesToDelete {
arrayOfB.removeAtIndex(indexToDelete - index)
}
if amount > 0
{
let name = arrayOfA[i].0 + " " + arrayOfA[i].1
arrayOfC += [(name, amount)]
}
}
You can try this.
for i in 0..<arrayOfA.count {
var amount = Int()
let name = arrayOfA[i].0 + " " + arrayOfA[i].1
let tempArr = arrayOfB.filter { (string) -> Bool in
string.rangeOfString(arrayOfA[i].1) != nil
}
amount = tempArr.count
if amount > 0 {
arrayOfC += [(name, amount)]
}
}
arrayOfC += [(name, amount)]
}
In any case filter should be more effective than for loop.

Swift, the for loop index value does not change as expectation

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!

outside array not affected from function appending within

I am working with arrays and I created a function that appends an array from within. However when I print the array, it still appears empty. What gives?
var queriesFinal : [String] = []
func queryValidator(search : String)
{
var letterSet = NSCharacterSet(charactersInString: "abcdefgjhijklmnopqrstuvwxyz ")
var numberSet = NSCharacterSet(charactersInString: "1234567890".uppercaseString)
var queriesTwo : [String] = search.lowercaseString.componentsSeparatedByCharactersInSet(letterSet)
for(var x = 0; x < queriesTwo.count; x++)
{
for(var y = 0; y < 10; y++)
{
var str = String(y)
if(queriesTwo[x] == str)
{
var numberStr = String(queriesTwo[x]) + "th"
queriesFinal.append(numberStr)
}
}
}
}
println(queriesFinal)
search = "Matt 8th"
queryValidator(search)
This code can run in playground..
I appreciate any help!
As mentioned by Mike S, you've made a small mistake println should be after your queryValidator, I've also added an optional in case your queryValidator search returns nil, also as mentioned by Zaph you don't need numberSet, so I removed it:
func queryValidator(search : String) -> [String]? {
let queriesTwo:[String] = search.lowercaseString.componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "abcdefgjhijklmnopqrstuvwxyz "))
var queriesResult:[String] = []
for x in 0...queriesTwo.count-1 {
for y in 0...9 {
if(queriesTwo[x] == String(y)) {
queriesResult.append(String(queriesTwo[x]) + "th")
}
}
}
return queriesResult.count > 0 ? queriesResult : nil
}
var search = "Matt 8 less7"
if let queriesFinal = queryValidator(search) {
println(queriesFinal)
} else {
println("no result")
}
An alternative approach with Regular Expressions:
func queryValidator(search: String) -> [String] {
var queriesFinal:[String] = []
var nsSearch: NSString = search
let pattern = "(\\d+)"
var regEx = NSRegularExpression(pattern:pattern, options:nil, error:nil)
regEx?.enumerateMatchesInString(nsSearch, options:nil, range:NSMakeRange(0, nsSearch.length), usingBlock: { (result, flags, stop) in
let found = nsSearch.substringWithRange(result.range)
queriesFinal.append("\(found)th")
})
return queriesFinal
}
var result = queryValidator(search)
println("result: \(result)")
Output:
result: [8th, 7th, 6th]
For information on regular expressions see: Regular Expressions

Resources