How to improve the speed of the code? - arrays

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.

Related

how to get Int from elements of dictionary array. xcode

let products = ["гречка" : "12,3,68", "рис" : "7,1,74", "овсянка" : "12,6,65"]
let userInput = "р"
for pair in products { //
if pair.key.contains(userInput) {
let elements = pair.value.components(separatedBy: ",")
print(pair.key + " contains \(elements [0])")
I need elements [0] take unary operation: var = 150 + element [0]
This?
let products = ["гречка" : "12,3,68", "рис" : "7,1,74", "овсянка" : "12,6,65"]
let userInput = "р"
for pair in products { //
if pair.key.contains(userInput) {
let elements = pair.value.components(separatedBy: ",")
let number = 150 + Int(elements[0])!
print(pair.key + " contains \(number)")
}
}

Swift 3: Restrict .components array and use reduce

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

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!

How would I go about categorizing items within an Array in swift?

In swift, I want to categorize items in an existing array and place them accordingly in one new string.
Here is an example of what I want to do:
originalArray = ["hotdog","fries","hotdog","coke","coke","fries","hotdog"]
resultingString = "hotdog x 3, fries x 2, coke x 2"
How would I go about doing this?
Try this:
let originalArray = ["hotdog","fries","hotdog","coke","coke","fries","hotdog"]
var dict = [String: Int]()
let resultString = originalArray.reduce(dict) { _, element in
if dict[element] == nil {
dict[element] = 1
} else {
dict[element]! += 1
}
return dict
}
.map { "\($0) x \($1)" }
.joinWithSeparator(", ")
If you want to keep the original order of the array (ie: hotdog, fries, coke), the code is slightly more complicated:
let originalArray = ["hotdog","fries","hotdog","coke","coke","fries","hotdog"]
var dict = [String: (index: Int, count: Int)]()
let resultString = originalArray.enumerate()
.reduce(dict) { _ , e in
if let value = dict[e.element] {
dict[e.element] = (index: value.index, count: value.count + 1)
} else {
dict[e.element] = (index: e.index, count: 1)
}
return dict
}
.sort { return $0.1.index < $1.1.index }
.map { "\($0) x \($1.count)" }
.joinWithSeparator(", ")
print(resultString)
I think this will help you:
let originalArray = ["hotdog","fries","hotdog","coke","coke","fries","hotdog"]
var resultingString = ""
var counts:[String:Int] = [:]
for item in originalArray {
counts[item] = (counts[item] ?? 0) + 1
}
resultingString = counts.map { (key, value) -> String in
return "\(key) x \(value)"
}.joinWithSeparator(",")
print(resultingString)
Here is the output: coke x 2, hotdog x 3, fries x 2

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