Swift 3 - Function for Wrapping an Array - arrays

As I try to learn Swift 3, I am attempting to "Wrap an Array of Elements". In Swift 3 Playgrounds, I was able to implement code that wraps an array. My problem occurs when I try to create a function that implements my code.
If you take the following code and copy-paste it to a Swift 3 Playground then you will likely see what I am trying to do. Change the selectedElement and on the right you will see the correct newArray. You can interchange the different elements for the selectedElement and the newArray will change accordingly.
I noted one of my failed attempts at turning this into a function.
import UIKit
let myArray = ["a", "b", "c", "d", "e"]
let selectedElement = "a"
//func arrayWrapper(inputArray: Array<String>) -> Array<String> {
var oldArray = [String]()
var priorElements = [String]()
var newArray = [String]()
for element in myArray {
if element == selectedElement || oldArray.count > 0 {
oldArray.append(element)
} else {
priorElements.append(element)
}
newArray = oldArray + priorElements
}
//return newArray
//}
priorElements
oldArray
oldArray + priorElements
newArray

Your method works fine.
The only problem I see is that you are trying to access the variables created inside the function outside of the function scope.
//priorElements
//oldArray
//
//oldArray + priorElements
arrayWrapper(inputArray: myArray)

Well, your non-function solution to this problem takes two inputs - the array you want to wrap, and the element you want it to wrap at. Therefore, your function should have two parameters as well:
// note the second parameter
func arrayWrapper(inputArray: Array<String>, selectedElemented: String) -> Array<String> {
var oldArray = [String]()
var priorElements = [String]()
var newArray = [String]()
for element in myArray {
if element == selectedElement || oldArray.count > 0 {
oldArray.append(element)
} else {
priorElements.append(element)
}
newArray = oldArray + priorElements
}
return newArray
}
Here is a more general version of this function, as an extension of Array:
extension Array where Element : Equatable {
func wrap(around selectedElement: Element) -> Array<Element> {
var oldArray = [Element]()
var priorElements = [Element]()
var newArray = [Element]()
for element in self {
if element == selectedElement || oldArray.count > 0 {
oldArray.append(element)
} else {
priorElements.append(element)
}
newArray = oldArray + priorElements
}
return newArray
}
}
// usage
myArray.wrap(around: selectedElement)

Related

Filtering Dictionary with an array of random Ints to make a new dict

So I have this method to get an array of random ints between 1-9, a random number of times between 1 and 7.
let n = arc4random_uniform(7) + 1
var arr: [UInt32] = []
for _ in 0 ... n {
var temp = arc4random_uniform(9) + 1
while arr.contains(temp) {
temp = arc4random_uniform(9) + 1
}
print(temp)
arr.append(temp)
}
print(arr)
So that gets me an array like [1,4,2] or [5,7,3,4,6]. And I have a method to turn another array of strings into a enumerated dictionary.
var someArray: [String] = ["War", "Peanuts", "Cats", "Dogs", "Nova", "Bears", "Pigs", "Packers", "Mango", "Turkey"]
extension Collection {
var indexedDictionary: [Int: Element] {
return enumerated().reduce(into: [:]) { $0[$1.offset] = $1.element }
}
}
let dict1 = someArray.indexedDictionary
print(dict1)
giving me the indexed dictionary
[1:"War", 2:"Peanuts",..etc]
MY question is using the Ints of the random array how do I create a new dictionary that only includes those keys and their values?
So for example if arr = [3,1,5]
how do I get a new dictionary of
[3:"dogs", 1:"Peanuts",5:"Bears"].
This should do it:
let finalDict = dict1.filter { arr.contains($0.key) }
Update:
You can even go a step further and skip the whole strings to array mapping. So remove
extension Collection {
var indexedDictionary: [Int: Element] {
return enumerated().reduce(into: [:]) { $0[$1.offset] = $1.element }
}
}
let dict1 = someArray.indexedDictionary
print(dict1)
and just use this:
Swift 4:
let finalArray = someArray.enumerated().flatMap { arr.contains($0.offset) ? $0.element : nil }
Swift 4.1:
let finalArray = someArray.enumerated().compactMap { arr.contains($0.offset) ? $0.element : nil }
Update 2:
If you need a dictionary and not an array in the end use this:
Swift 4:
let finalDict = someArray.enumerated().flatMap { randomInts.contains($0.offset) ? ($0.offset, $0.element) : nil }.reduce(into: [:]) { $0[$1.0] = $1.1 }
Swift 4.1:
let finalDict = someArray.enumerated().compactMap { randomInts.contains($0.offset) ? ($0.offset, $0.element) : nil }.reduce(into: [:]) { $0[$1.0] = $1.1 }

Use of unresolved identifier 'finalArray'?

var firstArray = ["1.","2.","3.","4."]
var secondArray = ["a","b","c"]
func combineTheArrays(array1: [Any], array2: [Any]) -> [Any] {
var finalArray = [Any]()
let maxIndex = array1.count >= array2.count ? array1.count : array2.count;
for i in 0...maxIndex{
if (array1.count > i){
finalArray.append(array1[i])
}
if (array2.count > i){
finalArray.append(array2[i])
}
} }
combineTheArrays(array1: firstArray, array2: secondArray)
print(finalArray)
I am trying to take two arrays with different/similar types and have it work through the function and combine into one single array. The ideal result of this func is to print:
finalArray = ["1.", "a", "2.", "b", "3.", "c", "4."]
You're very close! You just need to return finalArray at the end of your function definition, and then assign the result of the function call, so that you can then use it (such as in a print call):
let finalArray = combineTheArrays(array1: firstArray, array2: secondArray)
print(finalArray)
You should also use generics to ensure that you can handle any kind of elements, so long as their types are the same. Unlike returning Any, your result will be an array of the same type, which will be safer and easier to work with. Here is how I would improve this code:
func combineTheArrays<T>(array1: [T], array2: [T]) -> [T] {
let maxIndex = max(array1.count, array2.count);
var finalArray = [T]()
finalArray.reserveCapacity(array1.count + array2.count)
for i in 0..<maxIndex {
if i < array1.count { finalArray.append(array1[i]) }
if i < array2.count { finalArray.append(array2[i]) }
}
return finalArray
}
var firstArray = ["1.","2.","3.","4."]
var secondArray = ["a","b","c"]
let finalArray = combineTheArrays(array1: firstArray, array2: secondArray)
print(finalArray)

How eliminate duplicate objects from array of objects in swift

I am having two arrays coming from web service, I need to find out whether the Array2 has the same objects as Array1.
So, For this I am using below code:
var arr1 = [CustomObject]()
var arr2 = [CustomObject]()
var arr3 = [CustomObject]()
var arr4 = [CustomObject]()
self.arr3 = self.arr1 + self.arr2 //concatenate two arrays
self.arr4 = Array(Set(arr3)) // find out uniq values
// below is the extension
extension Array where Element : Hashable {
var unique: [Element] {
var uniqueValues: [Element] = []
forEach { item in
if !uniqueValues.contains(item) {
uniqueValues += [item]
}
}
return uniqueValues
}
}
But it is showing error on above line "Array(Set(arr3))"
Error Is :- To add value to Set
Try this :
var arr1 = ["A","B","C"]
var arr2 = ["A","B","C"]
if Set(arr1).symmetricDifference(arr2).isEmpty {
print("The Arrays Match")
}
Overview:
In order for the set to store custom class / struct, the custom class / struct needs to to conform to Hashable protocol and indirectly Equatable protocol.
Given below is an example using a struct, you can use a class as well.
Code:
struct CustomObject : Hashable{
var something : Int //It is just an example, this could be any type, but some how you should find a way to compute the hash value.
//MARK: Hashable
var hashValue: Int {
return something
}
}
//MARK: CustomObject - Equatable
func ==(lhs: CustomObject, rhs: CustomObject) -> Bool {
return lhs.something == rhs.something
}

Array reference/value

I have some arrays:
var arrayOne = [String]()
var arrayTwo = [String]()
var arrayThree = [String]()
Then I write a function. I give a number, it returns my one of these arrays:
func get_array(aNumber:Int) -> NSArray {
var arr = self.arrayOne //default
if aNumber == 1 {
arr = self.arrayOne
} else if aNumber == 2 {
arr = self.arrayTwo
} else if aNumber == 3 {
arr = self.arrayThree
}
return arr!
}
Then for instance I do:
var myArray = get_array(2)
myArray.append("hello")
NSLog("myArray: %#", myArray) // Ok, myArray was modified!!
NSLog("arrayTwo: %#", arrayTwo) // **Shit, arrayTwo was not modified!!** I was expecting myArray to be pointing to arrayTwo, so they are the same object
How can do this???
Swift arrays are value types. It means that
let array2 = array1
makes a copy of array1. So when you access array2 it is not the same array anymore as array1, it is another object with the same content.
This is different from NSArray and NSMutableArray which are a reference type: when you do
let nsarray2 = nsarray1
this time nsarray2 is a reference to nsarray1 and they are actually the same object.
In Swift Arrays are value types so they get copied if you assign, return or pass as parameter.
To make this work as expected you have to make a wrapper (class):
class ArrayWrapper<T> {
var array: [T]
init(_ array: [T] = []) {
self.array = array
}
}
var arrayOne = ArrayWrapper<String>()
var arrayTwo = ArrayWrapper<String>()
var arrayThree = ArrayWrapper<String>()
func get_array(aNumber:Int) -> ArrayWrapper<String> {
var arr = self.arrayOne //default
if aNumber == 1 {
arr = self.arrayOne
} else if aNumber == 2 {
arr = self.arrayTwo
} else if aNumber == 3 {
arr = self.arrayThree
}
return arr
}
// in case of your function I would suggest to use switch instead:
func getArray(aNumber:Int) -> ArrayWrapper<String> {
switch aNumber {
case 1: return self.arrayOne
case 2: return self.arrayTwo
case 3: return self.arrayThree
default: return self.arrayOne
}
}
// so you modify the Array as following:
var myArray = get_array(2)
myArray.array.append("hello")

Iterate over two arrays simultaneously

I am new to Swift. I have been doing Java programming. I have a scenario to code for in Swift.
The following code is in Java. I need to code in Swift for the following scenario
// With String array - strArr1
String strArr1[] = {"Some1","Some2"}
String strArr2[] = {"Somethingelse1","Somethingelse2"}
for( int i=0;i< strArr1.length;i++){
System.out.println(strArr1[i] + " - "+ strArr2[i]);
}
I have a couple of arrays in swift
var strArr1: [String] = ["Some1","Some2"]
var strArr2: [String] = ["Somethingelse1","Somethingelse2"]
for data in strArr1{
println(data)
}
for data in strArr2{
println(data)
}
// I need to loop over in single for loop based on index.
Could you please provide your help on the syntaxes for looping over based on index
You can use zip(), which creates
a sequence of pairs from the two given sequences:
let strArr1 = ["Some1", "Some2"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
for (e1, e2) in zip(strArr1, strArr2) {
print("\(e1) - \(e2)")
}
The sequence enumerates only the "common elements" of the given sequences/arrays. If they have different length then the additional
elements of the longer array/sequence are simply ignored.
With Swift 5, you can use one of the 4 following Playground codes in order to solve your problem.
#1. Using zip(_:_:) function
In the simplest case, you can use zip(_:_:) to create a new sequence of pairs (tuple) of the elements of your initial arrays.
let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
let sequence = zip(strArr1, strArr2)
for (el1, el2) in sequence {
print("\(el1) - \(el2)")
}
/*
prints:
Some1 - Somethingelse1
Some2 - Somethingelse2
*/
#2. Using Array's makeIterator() method and a while loop
It is also easy to loop over two arrays simultaneously with a simple while loop and iterators:
let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
var iter1 = strArr1.makeIterator()
var iter2 = strArr2.makeIterator()
while let el1 = iter1.next(), let el2 = iter2.next() {
print("\(el1) - \(el2)")
}
/*
prints:
Some1 - Somethingelse1
Some2 - Somethingelse2
*/
#3. Using a custom type that conforms to IteratorProtocol
In some circumstances, you may want to create you own type that pairs the elements of your initials arrays. This is possible by making your type conform to IteratorProtocol. Note that by making your type also conform to Sequence protocol, you can use instances of it directly in a for loop:
struct TupleIterator: Sequence, IteratorProtocol {
private var firstIterator: IndexingIterator<[String]>
private var secondIterator: IndexingIterator<[String]>
init(firstArray: [String], secondArray: [String]) {
self.firstIterator = firstArray.makeIterator()
self.secondIterator = secondArray.makeIterator()
}
mutating func next() -> (String, String)? {
guard let el1 = firstIterator.next(), let el2 = secondIterator.next() else { return nil }
return (el1, el2)
}
}
let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
let tupleSequence = TupleIterator(firstArray: strArr1, secondArray: strArr2)
for (el1, el2) in tupleSequence {
print("\(el1) - \(el2)")
}
/*
prints:
Some1 - Somethingelse1
Some2 - Somethingelse2
*/
#4. Using AnyIterator
As an alternative to the previous example, you can use AnyIterator. The following code shows a possible implementation of it inside an Array extension method:
extension Array {
func pairWithElements(of array: Array) -> AnyIterator<(Element, Element)> {
var iter1 = self.makeIterator()
var iter2 = array.makeIterator()
return AnyIterator({
guard let el1 = iter1.next(), let el2 = iter2.next() else { return nil }
return (el1, el2)
})
}
}
let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
let iterator = strArr1.pairWithElements(of: strArr2)
for (el1, el2) in iterator {
print("\(el1) - \(el2)")
}
/*
prints:
Some1 - Somethingelse1
Some2 - Somethingelse2
*/
Try This:
zip([0,2,4,6], [1,3,5,7]).forEach {
print($0,$1)
}
zip([0,2,4,6], [1,3,5,7]).forEach {
print($0.0,$0.1)
}
You could also enumerate over one array and used the index to look inside the second array:
Swift 1.2:
for (index, element) in enumerate(strArr1) {
println(element)
println(strArr2[index])
}
Swift 2:
for (index, element) in strArr1.enumerate() {
print(element)
print(strArr2[index])
}
Swift 3:
for (index, element) in strArr1.enumerated() {
print(element)
print(strArr2[index])
}
You could use Range if you still want to use for in.
var strArr1: [String] = ["Some1","Some2"]
var strArr2: [String] = ["Somethingelse1","Somethingelse2"]
for i in Range(start: 0, end: strArr1.count) {
println(strArr1[i] + " - " + strArr2[i])
}
for(var i = 0; i < strArr1.count ; i++)
{
println(strArr1[i] + strArr2[i])
}
That should do it. Never used swift before so make sure to test.
Updated to recent Swift syntax
for i in 0..< strArr1.count {
print(strArr1[i] + strArr2[i])
}
> Incase of unequal count
let array1 = ["some1","some2"]
let array2 = ["some1","some2","some3"]
var iterated = array1.makeIterator()
let finalArray = array2.map({
let itemValue = iterated.next()
return "\($0)\(itemValue != nil ? "-"+itemValue! : EmptyString)" })
// result : ["some1-some1","some2-some2","some3"]

Resources