Say I have two arrays:
let arrayOne = ["Hi", "Hi", "Hello", "Not Hey", "Howdy", "Hi"]
let arrayTwo = ["Hi", "Hello", "Hey", "Not Howdy", "Hi", "Hi"]
and I have this for loop that gets the percent similarity of the doubles:
var matches = 0
for (index, item) in enumerate(arrayOne) {
if item == arrayTwo[index] {
matches++
}
}
However, what if those arrays are longer and I want data points of those similarities instead of one single calculation. What kind of a function could I write where it takes the first 5 elements off the array, returns their similarity with the for loop, and moves on to the next 5? ie. it would be a function that takes two string arrays and returns an array of doubles. I am sure this is a simple question but I do not know how to approach taking 5 array elements at a time and then returning an array of doubles (like data points the the string array similarities).
I’m not clear quite what you’re asking, however, you might find playing around with zip, map, and reduce helpful.
For example, you could rewrite your original loop like this (assuming Swift 2.0, you’d have to rearrange slightly for 1.2):
zip(arrayOne, arrayTwo).reduce(0) { $0 + ($1.0 == $1.1 ? 1 : 0) }
// returns 4
zip creates a new sequence of the pairs of elements at each corresponding position.
reduce takes a starting value, then keeps a running value by applying a function to the current value and the next value in the sequence – bearing in mind this is a sequence of pair elements, you want to add 1 when they are the same, 0 when they aren’t. This then gives you a count of the positions where both match.
If instead you wanted an array, with true representing a match at that point, and false if different, you could use map:
zip(arrayOne, arrayTwo).map(==)
// returns [true, false, false, false, false, true]
If on the other hand you wanted a list of the differences in string length between the two strings at each position, you could change it to:
zip(arrayOne, arrayTwo).map { (a,b) in
a.characters.count - b.characters.count
}
// returns [0, -3, 2, -2, 3, 0]
As some have suggested, Set might help, e.g.:
let commonElements = Set(arrayOne).intersect(arrayTwo)
// returns strings present in both e.g. {"Hi", "Hello”}
This is a good approach if you are OK treating your data as a set i.e. order doesn’t matter and duplicates can be ignored. If order and dupes do matter, you probably have to stick with arrays.
Take a look at this. I think it does what you are asking. By introducing a count variable to keep track of the number of items you have processed, you will know when to update your counts array:
var matches = 0
let arrayOne = ["Hi", "Hi", "Hello", "Not Hey", "Howdy", "Hi", "a", "b", "c"]
let arrayTwo = ["Hi", "Hello", "Hey", "Not Howdy", "Hi", "Hi", "a", "B", "C"]
var count = 0
var counts:[Int] = []
for (index, item) in enumerate(arrayOne) {
if item == arrayTwo[index] {
matches++
}
// Have we done 5? If so, time to update counts array
if ++count == 5 {
counts.append(matches)
count = 0
matches = 0
}
}
// If we didn't have 5, just append the matches for the remaining items
if count > 0 {
counts.append(matches)
}
println(counts) // prints "[1, 2]"
Right - so I think I understand what you're looking for: you want to have a matches function like the one you've written that works for chunks of a certain number of elements. First off, you're going to need a chunk function. There's a good discussion of them here, but they're for arrays, and you're going to want to zip your two arrays together here, so you'll need one for SequenceType. This works:
public extension SequenceType {
/// Returns an array of arrays of n non-overlapping elements of self
/// - Parameter n: The size of the chunk
/// ```swift
/// [1, 2, 3, 4, 5].chunk(2)
///
/// [[1, 2], [3, 4], [5]]
/// ```
func chunk(_ n: Int) -> [[Generator.Element]] {
var g = self.generate()
var ret: [[Generator.Element]] = [[]]
while let next = g.next() {
if ret.last!.count < n {
ret[ret.endIndex.predecessor()].append(next)
} else {
ret.append([next])
}
}
return ret
}
}
Then, you need a function that counts the matches in two arrays. You could inline it with a closure, or you could define it separately, it doesn't make much of a difference.
func matchesEvery<
S0 : SequenceType,
S1 : SequenceType,
T : Equatable where
S0.Generator.Element == T,
S1.Generator.Element == T
>(_ n: Int, s0: S0, s1: S1) -> [Int] {
return zip(s0, s1)
.chunk(5)
.map { $0.reduce(0) { $1.0 == $1.1 ? $0 + 1 : $0 } }
}
That will return:
let arrayOne = ["Hi", "Hi", "Hello", "Not Hey", "Howdy", "Hi"]
let arrayTwo = ["Hi", "Hello", "Hey", "Not Howdy", "Hi", "Hi"]
matchesEvery(5, s0: arrayOne, s1: arrayTwo) // [1, 1]
Since in the first five there's one match, and in the last there is one as well.
Related
I am trying to pair the duplicate elements of an array and count the pairs.
When given array is : [10, 20, 20, 10, 10, 30, 50, 10, 20], I'm expecting numberOfPairs to be 3. Because there are 2 pairs of 10s and 1 pair of 20.
My "if condition" is checking if current element's index is first index or not. If it is not the last index, it means that there is a duplicate of the current element. So I'am adding 1 to numberOfPairs.
For input [10, 20, 20, 10, 10, 30, 50, 10, 20], my numberOfPairs is 2 but it should be 3.
For input [1 1 3 1 2 1 3 3 3 3], myNumberOfPairs is not printing at all? But instead it should be 4.
My What am I missing here?
func sockMerchant(n: Int, ar: [Int]) -> Int {
// Write your code here
var array = ar
var numberOfPairs = 0
for i in 0..<array.count {
var element = array[i]
let indexOfLastElement = array.lastIndex(of: element)
let indexOfFirstElement = array.firstIndex(of: element)
print("indexOfLastElement is \(indexOfLastElement)")
print("indexOfFirstElement is \(indexOfFirstElement)")
if indexOfFirstElement != indexOfLastElement {
numberOfPairs += 1
array.remove(at: indexOfFirstElement!)
array.remove(at: indexOfLastElement!)
continue
}
return numberOfPairs
}
return numberOfPairs
}
You're mutating your array by calling remove(at:) at the same time as you're accessing it which is why you're having these weird side effects.
I assume you're trying to solve a Leetcode task (or something similar), so I won't provide a solution upfront. My suggestion for you is to think of an algorithm that doesn't involve changing the contents of the List while you're reading these contents of that same List.
I'm agree with #MartinR that in such cases you should place breakpoints and go throught your code line by line, glad you've found your mistake by yourself.
But also in terms of performance, lastIndex and firstIndex are very heavy operations, because they may go thought all items and find nothing, which makes Big O notation of your algorithm around O(log n). In such cases dictionary is widely used(if you're not much limited with the space).
You can use value as a key and count as a value for a dictionary and count all items, then just sum like this:
func sockMerchant(ar: [Int]) -> Int {
ar.reduce(into: [Int:Int]()) { map, value in
map[value, default: 0] += 1
}.reduce(0) { sum, count in
sum + count.value / 2
}
}
So, I've solved the problem as below, thanks to #Vym and #Martin R.
func sockMerchant(n: Int, ar: [Int]) -> Int {
// Write your code here
var array = ar
var numberOfPairs = 0
var newArray = [Int]()
var done = false
for i in 0..<array.count {
let element = array[i]
let indexOfLastElement = array.lastIndex(of: element)
let indexOfFirstElement = array.firstIndex(of: element)
if indexOfFirstElement != indexOfLastElement {
newArray.append(element)
numberOfPairs = newArray.count/2
done = true
}
}
if done == true {
return numberOfPairs
}
return numberOfPairs
}
I would like to take an array of [String] and split it up into a given number of groups.
I have tried using this extension
extension Array {
func chunked(into size: Int) -> [[Element]] {
return stride(from: 0, to:count, by: size).map {
Array(self[$0 ..< Swift.min($0 + size, count)])
}
}
}
to split the array into a given number of elements per subarray, which for that function it works.
But to split it into a desired number of subarrays, I tried dividing the array.count by the desired number of teams, which works but only in certain circumstances.
If there are any extra elements, it puts them into an extra subarray at the end, and the number needs to come out even if I want this to work perfectly, which is the minority of the time.
So I guess this array.chunked function is not the solution in any way.
Maybe there is a way to do it with a for loop by taking an array.randomElement(), adding that to a variable (which would be a team) and then removing that element from the original array, and iterating over it until the original array is empty. And end up with an array of subarrays which would be the teams, or just separate variables which would be the teams. It could be any of those options.
Any ideas on how to do this?
Think about how you deal cards.
If you have 7 players, you start with one player and go around, giving one card at a time to each player. At the end, you may run out of cards before giving everybody the same number of cards. Some people may have n cards, and some may have n-1. That's the best that you can do.
You could implement the same thing with a source array and your destination arrays. Remove one element at a time from the source array, and "round-robbin" add it to one of the destination arrays until the source array is exhausted.
That code might look like this:
func splitArray<T>(array: [T], subArrayCount: Int) -> [[T]] {
// Create an empty array of arrays
var result = [[T]]()
// Create the empty inner string arrays
for _ in 1...subArrayCount {
let innerArray = [T]()
result.append(innerArray)
}
for (index, element) in array.enumerated() {
result[index % subArrayCount].append(element)
}
return result
}
And to test it:
let string = "Now is the time for all good programmers to babble incoherently. The rain in spain falls mainly on the plain. Fourscore and seven years ago our forefathers brought forth to this continent a new nation conceived in liberty and dedicated to the cause that all men are created equal."
let array = string.split(separator: " ")
.map { String($0) }
let subArrays: [[String]] = splitArray(array: array, subArrayCount: 5)
for (index, array) in subArrays.enumerated() {
let countString = String(format: "%2d", array.count)
print ("Array[\(index)]. Count = \(countString). Contents = \(array)")
}
The output of that test is:
Array[0]. Count = 10. Contents = ["Now", "all", "incoherently.", "falls", "Fourscore", "our", "this", "conceived", "to", "men"]
Array[1]. Count = 10. Contents = ["is", "good", "The", "mainly", "and", "forefathers", "continent", "in", "the", "are"]
Array[2]. Count = 10. Contents = ["the", "programmers", "rain", "on", "seven", "brought", "a", "liberty", "cause", "created"]
Array[3]. Count = 10. Contents = ["time", "to", "in", "the", "years", "forth", "new", "and", "that", "equal."]
Array[4]. Count = 9. Contents = ["for", "babble", "spain", "plain.", "ago", "to", "nation", "dedicated", "all"]
I have 2 Arrays. Say, array1 = [1,2,3,4,5] and array2 = [2,3]. How could I check in swift if array1 contains at least one item from array2?
You can do this by simply passing in your array2's contains function into your array1's contains function (or vice versa), as your elements are Equatable.
let array1 = [2, 3, 4, 5]
let array2 = [20, 15, 2, 7]
// this is just shorthand for array1.contains(where: { array2.contains($0) })
if array1.contains(where: array2.contains) {
print("Array 1 and array 2 share at least one common element")
} else {
print("Array 1 doesn't contains any elements from array 2")
}
This works by looping through array 1's elements. For each element, it will then loop through array 2 to check if it exists in that array. If it finds that element, it will break and return true – else false.
This works because there are actually two flavours of contains. One takes a closure in order to check each element against a custom predicate, and the other just compares an element directly. In this example, array1 is using the closure version, and array2 is using the element version. And that is the reason you can pass a contains function into another contains function.
Although, as correctly pointed out by #AMomchilov, the above algorithm is O(n2). A good set intersection algorithm is O(n), as element lookup is O(1). Therefore if your code is performance critical, you should definitely use sets to do this (if your elements are Hashable), as shown by #simpleBob.
Although if you want to take advantage of the early exit that contains gives you, you'll want to do something like this:
extension Sequence where Iterator.Element : Hashable {
func intersects<S : Sequence>(with sequence: S) -> Bool
where S.Iterator.Element == Iterator.Element
{
let sequenceSet = Set(sequence)
return self.contains(where: sequenceSet.contains)
}
}
if array1.intersects(with: array2) {
print("Array 1 and array 2 share at least one common element")
} else {
print("Array 1 doesn't contains any elements from array 2")
}
This works much the same as the using the array's contains method – with the significant difference of the fact that the arraySet.contains method is now O(1). Therefore the entire method will now run at O(n) (where n is the greater length of the two sequences), with the possibility of exiting early.
With Swift 5, you can use one of the following paths in order to find if two arrays have common elements or not.
#1. Using Set isDisjoint(with:) method
Set has a method called isDisjoint(with:). isDisjoint(with:) has the following declaration:
func isDisjoint(with other: Set<Element>) -> Bool
Returns a Boolean value that indicates whether the set has no members in common with the given sequence.
In order to test if two arrays have no common elements, you can use the Playground sample code below that implements isDisjoint(with:):
let array1 = [1, 3, 6, 18, 24]
let array2 = [50, 100, 200]
let hasNoCommonElement = Set(array1).isDisjoint(with: array2)
print(hasNoCommonElement) // prints: true
#2. Using Set intersection(_:) method
Set has a method called intersection(_:). intersection(_:) has the following declaration:
func intersection<S>(_ other: S) -> Set<Element> where Element == S.Element, S : Sequence
Returns a new set with the elements that are common to both this set and the given sequence.
In order to test if two arrays have no common elements or one or more common elements, you can use the Playground sample code below that implements intersection(_:):
let array1 = [1, 3, 6, 18, 24]
let array2 = [2, 3, 18]
let intersection = Set(array1).intersection(array2)
print(intersection) // prints: [18, 3]
let hasCommonElement = !intersection.isEmpty
print(hasCommonElement) // prints: true
An alternative way would be using Sets:
let array1 = [1,2,3,4,5]
let array2 = [2,3]
let set1 = Set(array1)
let intersect = set1.intersect(array2)
if !intersect.isEmpty {
// do something with the intersecting elements
}
Swift 5
Just make an extension
public extension Sequence where Element: Equatable {
func contains(anyOf sequence: [Element]) -> Bool {
return self.filter { sequence.contains($0) }.count > 0
}
}
Use:
let someArray = ["one", "two", "three"]
let string = "onE, Cat, dog"
let intersects = string
.lowercased()
.replacingOccurrences(of: " ", with: "")
.components(separatedBy: ",")
.contains(anyOf: someArray)
print(intersects) // true
let a1 = [1, 2, 3]
let a2 = [2, 3, 4]
Option 1
a2.filter { a1.contains($0) }.count > 1
Option 2
a2.reduce(false, combine: { $0 || a1.contains($1) })
Hope this helps.
//
// Array+CommonElements.swift
//
import Foundation
public extension Array where Element: Hashable {
func set() -> Set<Array.Element> {
return Set(self)
}
func isSubset(of array: Array) -> Bool {
self.set().isSubset(of: array.set())
}
func isSuperset(of array: Array) -> Bool {
self.set().isSuperset(of: array.set())
}
func commonElements(between array: Array) -> Array {
let intersection = self.set().intersection(array.set())
return intersection.map({ $0 })
}
func hasCommonElements(with array: Array) -> Bool {
return self.commonElements(between: array).count >= 1 ? true : false
}
}
I have a custom array used by a calculator, the array acts as an RPN stack. Many real life RPN calculators allow manipulation of the stack, and I am working on the same. To get an idea an example stack would be:
["1", "2", "+"] which would evaluate to 3. The core of it is the operations follow the operands.
["2", "1", "2", "+" "x"] evaluates to 6
["3.1415", "sin"] evaluates to 0
I would like to be able to "roll my stack" As in take the currently evaluated expression at the very end of the stack and roll it to the front. The problem is the operators, as the amount of binary and unary operators changes the way the stack is rotated.
["100", "1", "2", "+"]still would evaluate to 3, because 100 has not been accessed by an operator.
I need a way to recursively roll the stack, to take the last set of evaluated operands and roll it in front off all the operands yet to be evaluated.
Example:
["100", "1", "2", "+"] would roll to ["1", "2", "+", "100",] then ["100", "2", "1", "2", "+" "x"]would roll to ["2", "1", "2", "+" "x", "100"] and ["100", "3.1415", "sin"] would roll to ["3.1415", "sin","100"]
I am having problem with this, and the recursion of it. Currently I am doing this:
// safeRoll
func rollOpStack() {
var lastIndex = opStack.count - 1
var tmp: Op = opStack[lastIndex]
switch tmp {
case .UnaryOperation:
if opStack.count >= 2 {
var tmp2: Op = opStack[lastIndex - 1]
var appender: [Op] = [tmp2, tmp]
opStack.removeLast()
opStack.removeLast()
var appended: [Op] = appender + opStack
opStack = appended
}
case .BinaryOperation:
if opStack.count >= 3 {
var tmp2: Op = opStack[lastIndex - 1]
var tmp3: Op = opStack[lastIndex - 2]
var appender: [Op] = [tmp3, tmp2, tmp]
opStack.removeLast()
opStack.removeLast()
opStack.removeLast()
var appended: [Op] = appender + opStack
opStack = appended
}
default:
if opStack.count > 0 {
opStack.removeLast()
println(opStack)
opStack.insert(tmp, atIndex: 0)
}
}
}
This works... until more binary or unary operators are applied than just one, because the function is not recursive. How can I roll the stack recursively, and ensure that the entirety of the last evaluated list is rolled to the front of the stack? Thanks for the help.
This should do the trick:
func beginningIndexForOperationEndingAt(index: Int) -> Int? {
if index < 0 || index >= opStack.count {
return nil
}
switch opStack[index] {
case .UnaryOperation:
return beginningIndexForOperationEndingAt(index-1)
case .BinaryOperation:
if let firstOp = beginningIndexForOperationEndingAt(index-1) {
return beginningIndexForOperationEndingAt(firstOp-1)
}
return nil
default:
return index
}
}
If you call beginningIndexForOperationEndingAt(opStack.count-1) you will get the starting index for the last full operation, which you can then splice from the end of the array to the beginning
func rollOpStack() {
if let index = beginningIndexForOperationEndingAt(opStack.count-1) {
var newArray = opStack[index..<opStack.count]
newArray += opStack[0..<index]
var i = 0
for op in newArray {
opStack[i++] = op
}
} else {
//No complete operation...
}
}
Let me know if you run into any issues. Hope this helps! If it does, please don't forget to mark my answer as accepted! Thanks.
Swift has a great function .reduce that provides a running total for an entire array:
let array = [1, 2, 3, 4, 5]
let addResult = array.reduce(0) { $0 + $1 }
// addResult is 15
let multiplyResult = array.reduce(1) { $0 * $1 }
// multiplyResult is 120
But is there a simple way to use this for a running total only up to a specific element? For example for an array of [1, 2, 3, 4, 5] is there a way to use the .reduce function to return a running total for only up to the 3rd item (1+2+3=6)?
(I know I can always do a loop, just trying to learn all the possibilities with swift)
Besides using the reduce method of arrays, as in #MartinR's answer, it's also possible to use the global reduce function along with enumerate
The enumerate function returns a sequence of tuples, whose first element (named index) is the element index and the second (named element) is the element value.
So you can achieve the same result with the following code:
let sum = reduce(enumerate(array), 0) {
$0 + ($1.index < 3 ? $1.element : 0)
}
The advantage of this method is that you can apply more complex rules to exclude elements - for example, to sum all elements having even index:
let sum = reduce(enumerate(array), 0) {
$0 + ($1.index % 2 == 0 ? $1.element : 0)
}
The closure called by reduce() has no information about the
current index.
Theoretically it is possible to create a closure that
uses a captured variable to "remember" how often it has been called:
func addFirst(var count : Int) -> (Int, Int) -> Int {
return {
count-- > 0 ? $0 + $1 : $0
}
}
let array = [1, 2, 3, 4, 5]
let sumOfFirstThreeItems = array.reduce(0, addFirst(3) )
But this would still run over the whole array and not just the
first 3 elements. There is no way to "stop" the process.
A much simpler way is to operate on an array slice:
let array = [1, 2, 3, 4, 5]
let sumOfFirstThreeItems = array[0 ..< 3].reduce(0) { $0 + $1 }
A slice is an Array-like type that represents a sub-sequence of any
Array, ContiguousArray, or other Slice.