Swift - Search in array if contains string and append to another array - arrays

I have an array:
let arr = ["Ivan Ivanov", "Bogdan Bogdanov", "Georgi Milchev", "Bogdan Petkov", "Vladimir Zahariev"]
let name = "Bogdan"
Search if array contains(name) and append the result to the new array without loop.
So new array have to be ["Bogdan Bogdanov", "Bogdan Petkov"]
Trying with: if arr.contains(where: {$0 == name}) { newArray.append($0) }
but it's not working. Error: Anonymous closure argument not contained in a closure

You need
let res = arr.compactMap { $0.contains(name) ? $0.components(separatedBy: " ").last! : nil }

Related

Is it possible to iterate and replace values in an array using forEach?

I want to iterate an array of strings and replace each element with another string:
let arr = ["-","-","-"]
let spaceArr = replaceStringArr.forEach(replaceDash)
const replaceDash = function(index:any, array:any){
// replace the value of each element containing "-" with "&nbsp"
I have tried
return array[index] = "&nbsp"
and
array[index] = "&nbsp"
}
I have tried:
const replaceDash = function(index: any, array: any) {
return array[index] = "&nbsp"
}
as well as:
const replaceDash = function(index: any, array: any) {
array[index] = "&nbsp"
}
But I am getting a:
TypeError: Cannot create property '-' on number '0'
forEach receives the parameter currentValue first, index second, array third. You would need to modify the function as follows:
const replaceDash = function(item:any, index:any, array:any){
// replace the value of each element containing "-" with "&nbsp"
if(item === "-") array[index] = "&nbsp";
}
If I were you I'd also take a look at map function, which will give you a new array and seems to be more suited to what you want in less code:
let spaceArr = replaceStringArr.map(item => item === '-' ? '&nbsp' : item);
It seems you are misunderstanding for .forEach works and confusing it for .map.
.forEach just iterates through an array, it doesn't return anything, while .map actually returns the new modified array. If you want spaceArr to be the array with modified values, try this:
let arr = ["-","-","-"]
let spaceArr = arr.map(value => {return "&nbsp"})

How to Flatten Array of Array of custom Object [[CustomModel?]]?

I've only basic knowledge in Swift.
I want to change var dataSource:[[CustomModel?]]? into [CustomModel].
I tried following Methods
let flat = dataSource.reduce([],+)
let flat = dataSource.flatMap { $0 }
let flat = dataSource.compactMap{ $0 }
let flat = dataSource.Array(dataSource.joined())
I'm getting error
Cannot convert value of type '[FlattenSequence<[[CustomModel?]]>.Element]' (aka 'Array<Optional< CustomModel >>') to expected argument type '[CustomModel]'
You need to flat the nested array first using flatMap{}, then in order to get the non-optional value use compactMap{}. Suppose the input array is [[Int?]]
let value:[Int] = dataSource.flatMap{$0}.compactMap{ $0 } //Correct
The other option will give an error -
let value:[Int] = dataSource.flatMap{ $0 } ?? [] //Error
//Correct
//Wrong
You can try
var arr:[CustomModel] = dataSource?.flatMap { $0 } ?? []
Also
var arr:[CustomModel] = dataSource?.flatMap { $0 }.compactMap{ $0 } ?? []

Swift - Get first item in array, and return element and indices

I want to return the first item in a given array of custom objects and return an array of indices and the first custom object element so I can wrap a guard statement around it.
ie:
let firstOrder = existingOrders.enumerated().flatMap{$0,$1}.first
or attempt #1
let array = existingOrders.enumerated().map { (a, b) in return [$0.index : $1.element] }.first
or attempt #2
let array = existingOrders.enumerated().map { ($0.offset, $0.element) }.first
print (array)
This isn't returning the actual object; and it seems to return a tuple.
where
existingOrders = [ExistingOrder, EngineYard.ExistingOrder, EngineYard.ExistingOrder]
it returns the following;
[(0, EngineYard.ExistingOrder), (1, EngineYard.ExistingOrder), (2, EngineYard.ExistingOrder)]
attempt #3;
let array = existingOrders.enumerated().map { ($0.offset, $0.element) }
print (array)
guard let firstOrder = array.first else {
break
}
print (firstOrder) // should be a tuple of index and custom object
How do I grab the optional first item in an array and return index and element?
Many thanks
Edit. the reason I'm doing this is so that I can transfer the correct object to another class.
// transfer all
while (factory.existingOrders.count > 0) {
let array = myOrderBook.existingOrders.enumerated().map { ($0.offset, $0.element) }
guard let firstOrder = array.first else {
break
}
let index = (firstOrder.0)
factory.orderBook.transfer(index: index, destination: .completedOrder)
}
Where the Orderbook is a class;
Factory {
var orderBook:OrderBook = OrderBook()
}
OrderBook {
var existingOrders: [ExistingOrder] = [ExistingOrder]()
var completedOrders: [CompletedOrder] = [CompletedOrder]()
}
And the idea is that I want to transfer an object from existing orders to completed orders and vice versa
The function requires an index, but I guess I could refactor it so I can transfer an object instead.
The answer I was looking for was;
let array = myOrderBook.existingOrders.enumerated().map { ($0.offset, $0.element) }
However, I found that my code needed to be refactored.
Thanks.
Issue closed.

Reduce a string to a dictionary in Swift

What woudl be a simple way to reduce a string like AAA:111;BBB:222;333;444;CCC:555 to a dictionary in Swift. I have the following code:
var str = "AAA:111;BBB:222;333;444;CCC:555"
var astr = str.componentsSeparatedByString(";").map { (element) -> [String:String] in
var elements = element.componentsSeparatedByString(":")
if elements.count < 2 {
elements.insert("N/A", atIndex: 0)
}
return [elements[0]:elements[1]]
}
The code above produces an Array of Dictionaries:
[["A": "111"], ["BBB": "222"], ["UKW": "333"], ["UKW": "444"], ["CCC": "555"]]
I want it to produce
["A": "111", "BBB": "222", "UKW": "333", "UKW": "444", "CCC": "555"]
but no mater what I try, since i call the map function on an Array it seems impossible to convert the nature of the map function's result.
NOTE: The dictionary in string format is described as either having KEY:VALUE; format or VALUE; format, in which case the mapping function will add the "N/A" as being the key of the unnamed value.
Any help on this matter is greatly appreciated.
Your map produces an array of dictionaries. When you want to combine them into 1, that's a perfect job for reduce:
func + <K,V>(lhs: Dictionary<K,V>, rhs: Dictionary<K,V>) -> Dictionary<K,V> {
var result = Dictionary<K,V>()
for (key, value) in lhs {
result[key] = value
}
for (key, value) in rhs {
result[key] = value
}
return result
}
var str = "AAA:111;BBB:222;333;444;CCC:555"
var astr = str
.componentsSeparatedByString(";")
.reduce([String: String]()) {
aggregate, element in
var elements = element.componentsSeparatedByString(":")
if elements.count < 2 {
elements.insert("N/A", atIndex: 0)
}
return aggregate + [elements[0]:elements[1]]
}
print(astr)
Swift has no default operator to "combine" two Dictionaries so you have to define one. Note that the + here is not commutative: dictA + dictB != dictB + dictA. If a key exist in both dictionaries, the value from the second dictionary will be used.
This is a work for reduce:
let str = "AAA:111;BBB:222;333;444;CCC:555"
let keyValueStrings = str.componentsSeparatedByString(";")
let dictionary = keyValueStrings.reduce([String: String]()) {
aggregate, element in
var newAggregate = aggregate
let elements = element.componentsSeparatedByString(":")
let key = elements[0]
// replace nil with the value you want to use if there is no value
let value = (elements.count > 1) ? elements[1] : nil
newAggregate[key] = value
return newAggregate
}
print(dictionary)
You can also make aggregate mutable directly:
let dictionary = keyValueStrings.reduce([String: String]()) {
(var aggregate: [String: String], element: String) -> [String: String] in
let elements = element.componentsSeparatedByString(":")
let key = elements[0]
// replace nil with the value you want to use if there is no value
let value = (elements.count > 1) ? elements[1] : nil
aggregate[key] = value
return aggregate
}
This is a functional approach, but you can achieve the same using a for iteration.
The reason this is happening is because map can only return arrays. If you are using this method to parse your string, then you need to convert it to a dictionary after.
var newDict = [String:String]()
for x in astr {
for (i, j) in x {
newDict[i] = j
}
}
The current issue with your code is that map function iterates over array containing [["key:value"],["key:value"]..] and you separate it again. But it returns ["key":"value"] which you then add to your array.
Instead you can add elements[0]:elements[1] directly to a locally kept variable which will fix your problem. Something like
finalVariable[elements[0]] = elements[1]

SWIFT String? does not have a member named 'element'

I would like to know how can i fill my label from an Array
func metaDataUpdated(metaData : NSString){
var listItems: NSArray = [metaData.componentsSeparatedByString(";")]
if ([listItems.count] > 0){
println([listItems.objectAtIndex(0)])
titleSong.text = [listItems.objectAtIndex(0)]
}
}
I don't really know how to convert an array to string.
Direct conversion to Swift:
func metaDataUpdated(metaData : String) {
let listItems = metaData.componentsSeparatedByString(";")
if listItems.count > 0 {
print(listItems[0])
titleSong.text = listItems[0]
}
}
Nicer Swift:
func metaDataUpdated(metaData : String) {
let listItems = metaData.componentsSeparatedByString(";")
if let first = listItems.first {
print(first)
titleSong.text = first
}
}
Even nicer Swift, without using Foundation and without the function needing to get every component separated by ";", but only the first one (recommended):
func metaDataUpdated(metaData : String) {
if let index = metaData.characters.indexOf(";") {
let first = metaData[metaData.startIndex ..< index]
print(first)
titleSong.text = first
}
}
you cannot assign NSArray to NSString therefore you need to cast the value of this first index into a string
change this
titleSong.text = [listItems.objectAtIndex(0)]
to
titleSong.text = "\(listItems.objectAtIndex(0))"
or
titleSong.text = listItems[0] as! String
and also change this line to ([listItems.count > 0]) to (listItems.count > 0)
your code will look like this:
Note this not obj-c so remove all []
func metaDataUpdated(metaData : NSString){
var listItems: NSArray = metaData.componentsSeparatedByString(";")
if (listItems.count > 0)
{
println(listItems.objectAtIndex(0))
titleSong.text = listItems.objectAtIndex(0) as! String
}
}
Better use Swift types and objects now: Array instead of NSArray, Dictionary instead of NSDictionary, etc.
func metaDataUpdated(metaData : NSString) {
var listItems = metaData.componentsSeparatedByString(";")
if listItems.count > 0 {
print(listItems[0])
titleSong.text = listItems[0]
}
}
Here componentsSeparatedByString returns an array of strings: [String]. We then use simple index subscripting to retrieve its first value.
Note: I suppose you were trying to adapt code from Objective-C because your example was ridden with [] everywhere...
Put your to string item in "\\()".
For instance:
titleSong.text = "\\([listItems.objectAtIndex(0)])"
Not sure you need the [] brackets though

Resources