3
struct Product {
    let name: String
    let weight: Double
}

let productsList = [Product(name: "AAA", weight: 1),
                    Product(name: "BBB", weight: 2),
                    Product(name: "CCC", weight: 3),
                    Product(name: "DD", weight: 4),
                    Product(name: "RR", weight: 5),
                    Product(name: "EEE", weight: 6),
                    Product(name: "FGT", weight: 7),
                    Product(name: "DSF", weight: 8),
                    Product(name: "VCVX", weight: 9),
                    Product(name: "GFDHT", weight: 10)]

print(productsList.map { $0.name })

I am getting all the product names from the above line but I want to get only names from odd indexes using map. Is it possible?

par
  • 17,361
  • 4
  • 65
  • 80

2 Answers2

4

Here's one way of doing this:

print(
    productsList
        .enumerated()
        .filter { $0.offset % 2 == 1 }
        .map { $0.element.name }
)

The enumerated method turns each product into a 2-tuple that contains both the product and its index in the array (offset). You then filter to leave only the products that have an odd (or even) index. After that, you need to map the 2-tuples to the name of the product.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • `.filter { $0.offset & 1 == 1 }` might be faster, but great answer. +1 – par Jun 24 '18 at 08:21
  • 1
    and to completely answer the question your `map` should return `$0.element.name` – par Jun 24 '18 at 08:24
  • @par Ah! missed that part of the question. – Sweeper Jun 24 '18 at 08:30
  • @par: I strongly assume that the compiler generates the same machine code for `$0.offset % 2 == 1` and `$0.offset & 1 == 1` – Martin R Jun 24 '18 at 08:46
  • @MartinR `%` produces about 5 times as many instructions (disassemble it and you'll see). At a minimum the mod operator has to load both the dividend and the divisor into a register which takes time. `& 1` generates a single and-with-immediate instruction. – par Jun 25 '18 at 00:28
  • @par: The compiler does in fact create “more code” for `x % 2 == 1`, but the reason is that the result of `x % 2` can be 0, 1, or -1, depending on the sign of x. If you change it to `x % 2 != 0` then the *identical* code is created as for `x % 1 == 1` (when compiling with optimizations, verified with "swiftc -O -emit-assembly ...") – Martin R Jun 25 '18 at 05:36
  • 1
    @MartinR Thank you for finding this, it's interesting (as are so many of your SO answers!). For my (admittedly lazy) self, I'm not sure having to remember to invert the test logic and enable optimizations to get the same code is any easier than knowing I can just look at the low bit for even/oddness and always get good results. I'll admit it feels more natural to look at the remainder of a divide-by-two for that test, but as a matter of old habit I tend to take simple gains where I can find them. – par Jun 25 '18 at 09:33
2

You can map the odd indices to the corresponding product name:

let oddIndexedProducts = stride(from: 1, through: productsList.count, by: 2)
    .map { productsList[$0].name }

print(oddIndexedProducts) // ["BBB", "DD", "EEE", "DSF", "GFDHT"]

Another way is to use compactMap on the enumerated() sequence:

let oddIndexedProducts = productsList.enumerated().compactMap {
    $0.offset % 2 == 1 ? $0.element.name : nil
}

As a rule of thumb: “filter + map = compactMap” (“flatMap” in Swift 3).

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Just to make sure that I got it right, it seems to be the older `flatMap` functionality, correct? and yes, I checked: https://stackoverflow.com/questions/49291057/difference-between-flatmap-and-compactmap-in-swift – Ahmad F Jun 24 '18 at 08:34
  • 1
    @Ahmad Yes, that was renamed in Swift 4.1. – Martin R Jun 24 '18 at 08:37