Skip Item When Performing Map in Swift

Skip item when performing map in Swift?

Just replace map() by flatMap():

extension SequenceType {
/// Returns an `Array` containing the non-nil results of mapping
/// `transform` over `self`.
///
/// - Complexity: O(*M* + *N*), where *M* is the length of `self`
/// and *N* is the length of the result.
@warn_unused_result
public func flatMap<T>(@noescape transform: (Self.Generator.Element) throws -> T?) rethrows -> [T]
}

try? ... returns nil if the call throws an error, so those
elements will be omitted in the result.

A self-contained example just for demonstration purposes:

enum MyError : ErrorType {
case DivisionByZeroError
}

func inverse(x : Double) throws -> Double {
guard x != 0 else {
throw MyError.DivisionByZeroError
}
return 1.0/x
}

let values = [ 1.0, 2.0, 0.0, 4.0 ]
let result = values.flatMap {
try? inverse($0)
}
print(result) // [1.0, 0.5, 0.25]

For Swift 3, replace ErrorType by Error.

For Swift 4 use compactMap

Swift Array Map Skip Value

As @LeoDabus pointed out you should use flatMap instead of map in this situation. The reason is because flatMap can return nil since flatMap has the return type U? while map has the return type of U and can´t handle nil.

So this line, can handle nil with flatMap:

let finalArray = myArray.flatMap({ id in numbers.first(where: {$0 == id}) })

RxSwift how to skip map depending on previous result?

This is a bit of a tough question to answer because on the one hand you ask a bog simple question about skipping a map while on the other hand you ask for "most RxSwift idiomatic way of doing this," which would require more changes than simply jumping the map.

If I just answer the basic question. The solution would be to have checkModel return a Maybe rather than a Single.


Looking at this code from a "make it more idiomatic" perspective, a few more changes need to take place. A lot of what I'm about to say comes from assumptions based on the names of the functions and expectations as to what you are trying to accomplish. I will try to call out those assumptions as I go along...

The .observe(on: ConcurrentDispatchQueueScheduler(qos: .background)) is likely not necessary. URLSession already emits on the background.

The parseJson function probably should not return an Observable type at all. It should just return a ModelObject. This assumes that the function is pure; that it doesn't perform any side effect and merely transforms a Data into a ModelObject.

func parseJson(_ data: Data) throws -> ModelObject

The checkModel function should probably not return an Observable type. This really sounds like it should return a Bool and be used to filter the model objects that don't need further processing out. Here I'm assuming again that the function is pure, it doesn't perform any side-effect, it just checks the model.

func checkModel(_ modelObject: ModelObject) -> Bool

Lastly, the processObject function presumably has side effects. It's likely a consumer of data and therefore shouldn't return anything at all (i.e., it should return Void.)

func processObject(_ modelObject: ModelObject)

Udpdate: In your comments you say you want to end with a Completable. Even so, I would not want this function to return a completable because that would make it lazy and thus require you to subscribe even when you just want to call it for its effects.

You can create a generic wrap operator to make any side-effecting function into a Completable:

extension Completable {
static func wrap<T>(_ fn: @escaping (T) -> Void) -> (T) -> Completable {
{ element in
fn(element)
return Completable.empty()
}
}
}

If the above functions are adjusted as discussed above, then the Observable chain becomes:

let getAndProcess = URLSession.shared.rx.data(request:request)
.map(parseJson)
.filter(checkModel)
.flatMap(Completable.wrap(processObject))
.asCompletable()

The above will produce a Completable that will execute the flow every time it's subscribed to.

By setting things up this way, you will find that your base functions are far easier to test. You don't need any special infrastructure, not even RxText to make sure they are correct. Also, it is clear this way that parseJson and checkModel aren't performing any side effects.

The idea is to have a "Functional Core, Imperative Shell". The imperative bits (in this case the data request and the processing) are moved out to the edges while the core of the subscription is kept purely functional and easy to test/understand.

Map or reduce with index in Swift

You can use enumerate to convert a sequence (Array, String, etc.) to a sequence of tuples with an integer counter and and element paired together. That is:

let numbers = [7, 8, 9, 10]
let indexAndNum: [String] = numbers.enumerate().map { (index, element) in
return "\(index): \(element)"
}
print(indexAndNum)
// ["0: 7", "1: 8", "2: 9", "3: 10"]

Link to enumerate definition

Note that this isn't the same as getting the index of the collection—enumerate gives you back an integer counter. This is the same as the index for an array, but on a string or dictionary won't be very useful. To get the actual index along with each element, you can use zip:

let actualIndexAndNum: [String] = zip(numbers.indices, numbers).map { "\($0): \($1)" }
print(actualIndexAndNum)
// ["0: 7", "1: 8", "2: 9", "3: 10"]

When using an enumerated sequence with reduce, you won't be able to separate the index and element in a tuple, since you already have the accumulating/current tuple in the method signature. Instead, you'll need to use .0 and .1 on the second parameter to your reduce closure:

let summedProducts = numbers.enumerate().reduce(0) { (accumulate, current) in
return accumulate + current.0 * current.1
// ^ ^
// index element
}
print(summedProducts) // 56

Swift 3.0 and above

Since Swift 3.0 syntax is quite different.

Also, you can use short-syntax/inline to map array on dictionary:

let numbers = [7, 8, 9, 10]
let array: [(Int, Int)] = numbers.enumerated().map { ($0, $1) }
// ^ ^
// index element

That produces:

[(0, 7), (1, 8), (2, 9), (3, 10)]

How to skip an index in a for loop in Swift

The index is automatically incremented in the loop, you can skip an index with a where clause:

for index in 0..<5 where index != 2 {
print(index)
}

Swift map() function with an 'if' condition

You can use map() with an if-condition inside the closure:

var arr = [11, 12, 13, 14, 15]
arr = arr.map { elem in
if elem == 15 { return 1 } else { return 0 }
}
print(arr) // [0, 0, 0, 0, 1]

Using the conditional operator ?: and closure shorthand notation $0,
this can be simplified to

arr = arr.map { $0 == 15 ? 1 : 0 }

map() calls the closure with each element in turn,
and returns an array with the closure return values.
Inside the closure, $0 is the current argument, and the return value
is 1 or 0, depending on the boolean condition.

Exclude element in array when iterating using map

I'd simply do a filter as described as your problem, you want to filter the numbers by removing another number.

var myNums = [1, 2, 3, 4, 5]
let excludeNums = [2]

let newNum = myNums.filter({ !excludeNums.contains($0) })

print(newNum) //1, 3, 4, 5

If you need to do a map, you could do a map first then filter.

let newNum = myNums.map({ $0*2 }).filter({ !excludeNums.contains($0) })
print(newNum) //4, 6, 8, 10

This maps to multiplying both by 2 and then filtering by removing the new 2 from the list. If you wanted to remove the initial 2 you would have to filter first then map. Since both return a [Int] you can call the operations in any order, as you deem necessary.

skip to item of index number... on iOS Media Player with Swift

The solution is, creating a collection as MPMediaItemCollection, to set the song to play with : player.nowPlayingItem = collection.items[1]

for example :

var query = MPMediaQuery.songsQuery()
var collection = MPMediaItemCollection(items: query.items!) //it needs the "!"
let player = MPMusicPlayerController.applicationMusicPlayer()

player.setQueueWithItemCollection(collection)

player.nowPlayingItem = collection.items[1]

Note : Ifound the answer here : Play Song at specific index of MPMediaItemCollection in Swift

What's the cleanest way of applying map() to a dictionary in Swift?

Swift 4+

Good news! Swift 4 includes a mapValues(_:) method which constructs a copy of a dictionary with the same keys, but different values. It also includes a filter(_:) overload which returns a Dictionary, and init(uniqueKeysWithValues:) and init(_:uniquingKeysWith:) initializers to create a Dictionary from an arbitrary sequence of tuples. That means that, if you want to change both the keys and values, you can say something like:

let newDict = Dictionary(uniqueKeysWithValues:
oldDict.map { key, value in (key.uppercased(), value.lowercased()) })

There are also new APIs for merging dictionaries together, substituting a default value for missing elements, grouping values (converting a collection into a dictionary of arrays, keyed by the result of mapping the collection over some function), and more.

During discussion of the proposal, SE-0165, that introduced these features, I brought up this Stack Overflow answer several times, and I think the sheer number of upvotes helped demonstrate the demand. So thanks for your help making Swift better!



Related Topics



Leave a reply



Submit