Filtering an array from another - arrays

"Is it possible to remove for .. in (to make it more Swifty) ?
let arr1 = ["Bill", "John", "Richard", "Donald", "George"]
let arr2 = ["ill", "ck", "rd", "ld"]
var arr3 = Array<String>()
for str in arr2
{
arr3 += arr1.filter{ $0.hasSuffix(str) }
}
debugPrint(arr3) // ["Bill", "Richard", "Donald"]
Thank you !

Yes it is. Use the map function in conjunction with flatMap. If you want no doubled (e. g. when suffix is "d"), use Set():
let names = ["Bill", "John", "Richard", "Donald", "George"]
let suffixes = ["ill", "ck", "rd", "ld", "d"]
let namesWithSuffixes = Set(suffixes.map({ suffix in names.filter({ name in name.hasSuffix(suffix) }) }).flatMap({ $0 }))
But if you just want to get rid of the old for loop, you can use forEach():
var arr3 = [String]()
suffixes.forEach({ suffix in
arr3 += names.filter({ $0.hasSuffix(suffix) })
})
Be aware that your original solution and the second one only causes 24 executions while the "swifty" map.filter.flatmap uses 29 executions.

Wouldn't this be more concise and straightforward ?
arr3 = arr1.filter{name in arr2.contains{name.hasSuffix($0)}}

Related

compare multiple arrays for same elements in swift

I'm new to swift and programming in general. I have multiple arrays of names in a database and I need to check for same names in these arrays.
I've found some solutions to compare two arrays, but not multiple so I wrote some additional code.
But performance wise it's not the best practice I think. And also not the best way to add first all the names and then remove the duplicates..
Does anyone has any better ideas/solutions for my problem?
Code:
import UIKit
let array1 = ["Max", "Peter","Kathrin", "Sara", "Kirsten", "Mike", "Elon"] // Peter, Kathrin, Mike, Sara
let array2 = ["Pamela", "Chris", "James", "Sebastian", "Mike"] // Mike, Chris
let array3 = ["John", "Daniel", "Susan", "Mathias", "Mike", "Donald"] // Mike
let array4 = ["Tim", "Kathrin", "Alan", "Chris", "Amy", "Sara"] // Kathrin, Chris
let array5 = ["Cara", "Charly", "Emily", "Maja", "Peter", "Sara"] // Peter, Sara
// Output should be: Peter, Kathrin, Mike, Sara, Chris
var array = [Array<String>]()
array.append(array1)
array.append(array2)
array.append(array3)
array.append(array4)
array.append(array5)
var names = [String]()
for i in 0...array.count - 2 {
for z in 1...array.count - 1 {
if z + i < array.count {
let commonElements = Array(Set(array[i]).intersection(Set(array[z+i])))
names.append(contentsOf: commonElements)
}
}
}
print(names.removeDuplicates())
Extension:
extension Array where Element: Hashable {
func removeDuplicates() -> [Element] {
var result = [Element]()
for value in self {
if result.contains(value) == false {
result.append(value)
}
}
return result
}
}
If your intent is to just check if a name occurs in more than one collection I think the best way to approach this is creating a single collection with all the names and filter the duplicates as shown in this post
let array1 = ["Max", "Peter","Kathrin", "Sara", "Kirsten", "Mike", "Elon"]
let array2 = ["Pamela", "Chris", "James", "Sebastian", "Mike"]
let array3 = ["John", "Daniel", "Susan", "Mathias", "Mike", "Donald"]
let array4 = ["Tim", "Kathrin", "Alan", "Chris", "Amy", "Sara"]
let array5 = ["Cara", "Charly", "Emily", "Maja", "Peter", "Sara"]
var names: [String] = []
names.append(contentsOf: array1)
names.append(contentsOf: array2)
names.append(contentsOf: array3)
names.append(contentsOf: array4)
names.append(contentsOf: array5)
extension RangeReplaceableCollection where Element: Hashable {
var duplicates: Self {
var set: Set<Element> = []
var filtered: Set<Element> = []
return filter { !set.insert($0).inserted && filtered.insert($0).inserted }
}
}
// Output should be: Peter, Kathrin, Mike, Sara, Chris
print(names.duplicates) // ["Mike", "Kathrin", "Chris", "Sara", "Peter"]

Compare an array with another & then return the index of matched results

Sorry if this is a dumb question but everything I try seems to be wrong! (I am new to Swift).
I have 2 arrays of Strings which I need to compare for matches and then return an array with the index position of those matches .... IE:
let array1 = ["abc", "def", "ghi", "jkl", "xyz", "uhr"]
let array2 = ["ghi", "xyz", "uhr"]
// Search array1 for instances of array2
// Result I want is: [2, 4, 5]
Is there a simple function I am missing?! Thanks in advance for your help.
For an efficient solution you can create an index first. The index maps each element in the first array to its position in the array. Both arrays are traversed only once:
let array1 = ["abc", "def", "ghi", "jkl", "xyz", "uhr"]
let array2 = ["ghi", "xyz", "uhr"]
let index = Dictionary(uniqueKeysWithValues: array1.enumerated().map {
($0.element, $0.offset)
})
let result = array2.compactMap { index[$0] }
print(result) // [2, 4, 5]
If the elements in array1 are not known to be unique then the index must be computed slightly differently:
let index = Dictionary(array1.enumerated().map { ($0.element, $0.offset) },
uniquingKeysWith: { (first, _) in first })
The second parameter is a closure which controls which value to put into the dictionary in the case of duplicate keys. Here we choose the position of the first occurrence of an element in the array.
var indexArr: [Int] = []
for element in array2 {
if let elementIndex = array1.firstIndex(of: element) {
indexArr.append(elementIndex)
}
}
print(indexArr)
var results: [Int] = []
for i in 0..<array1.count {
for j in 0..<array2.count {
if array1[i] == array2[j] {
results.append(i)
}
}
}
print(results)

merging element of two different arrays into dictionary in swift

i have two arrays like these
var arr1 = ["han", "Ji", "Kidda", "Ho", "Tusi"]
var arr2 = ["hello", "Ji"]
i want to create a new dictionary that have first element of first array and first element of second array and so on. when the third element of first array comes it should again get the first element of second array.
for example:-
dict = ["han" : "hello", "Ji" : "Ji", "Kidda" : hello, "Ho" : "Ji", "Tusi" : "hello"]
If the second array has 2 items you can do
var dict = [String: String]()
for (index, item) in arr1.enumerated() {
dict[item] = arr2[index % 2]
}
I believe this is what you're looking for (using arr1 as the keys and arr2 as the values repeating them as necessary):
var arr1 = ["han", "Ji", "Kidda", "Ho", "Tusi"]
var arr2 = ["hello", "Ji"]
let dict = Dictionary(uniqueKeysWithValues: zip(arr1, arr1.indices.map { arr2[$0 % arr2.count] }))
print(dict)
["Kidda": "hello", "Ji": "Ji", "han": "hello", "Ho": "Ji", "Tusi": "hello"]
Note:
Dictionaries have no specified ordering. Only the key/value pairings matter. This matches the example in your question.
Explanation:
zip is used to create a sequence of (key, value) tuples from two sequences that will become the key/value pairs for the new Dictionary. The keys come from arr1. map is used to generate the sequence of values from arr2 repeating them as many times as necessary to match the count of arr1. This sequence of (key, value) tuples is passed to Dictionary(uniqueKeysWithValues:) to turn that sequence into the desired Dictionary.
try:
var dict = ["arr1" : "hello", "arr2" : "Ji"]
then for third you can append by
dict[3] = ["arr3" : String(arr3.first())]
Try this:
var arr1 = ["han", "Ji", "Kidda", "Ho", "Tusi"]
var arr2 = ["hello", "Ji"]
var dict : [String : String] = [:]
var arr2Index = 0
for index in 0..<arr1.count {
let arr1Value = arr1[index]
if arr2Index == arr2.count {
arr2Index = 0
}
let arr2Value = arr2[arr2Index]
dict[arr1Value] = arr2Value
arr2Index += 1
}
Here's a fun way:
let arr1 = ["han", "Ji", "Kidda", "Ho", "Tusi"]
let arr2 = ["hello", "Ji"]
let arr3 = Array(repeating: arr2, count: arr1.count).joined()
let d = zip(arr1,arr3).reduce(into: [String:String]()) { $0[$1.0] = $1.1 }

How to group items from array that match, into another array?

For example say I have an array like so:
var someArray = ["1", "1", "2"]
I need to put this into two arrays that look like:
["1","1"]
["2"]
How can I go about this?
Any help would be great!
Use Dictionary initializer init(grouping:by:)
Then just get arrays by accessing values property.
Example:
let dic = Dictionary(grouping: someArray) { $0 }
let values = Array(dic.values)
print(values)
Result:
[["2"], ["1", "1"]]
Here are some facts (the upvote and answer should go to #kirander)
With #kirander method's is using the Dictionary to map the objects in a O(N) runtime and O(N) memory.
The other solutions are mostly running in O(N*N) runtime and O(N) memory. Because of this, grouping a random array of 1000 items will take: 0.07s with #kirander solution and 34s. with other solutions.
func benchmark(_ title:String, code: ()->()) {
let startTime = CFAbsoluteTimeGetCurrent()
code()
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("Time elapsed for \(title): \(timeElapsed) s.")
}
var array:[String] = []
for _ in 0...1000 {
array.append("\(Int(arc4random_uniform(10)))")
}
// #kirander solution 0.07s
benchmark("Dictionary", code: {
let dic = Dictionary(grouping: array, by: { $0 })
let values = Array(dic.values)
})
// #Bruno solution ~34s
benchmark("Array", code: {
var resultingArrays = [[String]]()
for value in array {
let ar = array.filter({ $0 == value })
if !resultingArrays.contains(where: {$0 == ar}) {
resultingArrays.append(ar)
}
}
})
You could try something like this:
var someArray = ["1", "1", "2"]
var resultingArrays = [[String]]()
for value in someArray {
let array = someArray.filter({ $0 == value })
if !resultingArrays.contains(where: {$0 == array}) {
resultingArrays.append(array)
}
}
You can try this one :
let arrM = ["1","3","4","6","1","1","3"]
let arrSrtd = Array(Set(arrM))
for ele in arrSrtd{
let a = arrM.filter( {$0 == ele})
print(a)
}

Faster way to get multiple elements from an array in Swift?

Is there a faster/more concise way to get multiple indexes from an array besides looping, and appending? Maybe a one-liner functional variant of the following?
let names: [String] = ["John", "Mary", "Hugo", "Bill", "Andrea"]
let indexesToGet = [0, 1, 3]
var result: [String] = []
for i in 0..<indexesToGet.count {
result.append(names[indexesToGet[i]])
}
return result
//returns ["John", "Mary", "Bill"]
You can try like this.
let result = indexesToGet.map { names[$0] }
To prevents from indexOutOfBounds crash you can use flatMap.
let result = indexesToGet.flatMap { (names.count > $0) ? names[$0] : nil}
From Swift 4.1 use compactMap instead of flatMap.
let result = indexesToGet.compactMap { (names.count > $0) ? names[$0] : nil}

Resources