How to map only non-empty values? - arrays

I am building an iOS app and I got an array with textfields and I want to map the text of these fields into an array.
This is what I try and it works but I only want to map a value if it is not empty. Now I get all even if the textfield is empty.
textFields.map{$0.text!}
Update
I solved it like this
textFields.filter({$0.text! != ""}).map({$0.text!})

The current answers both involve two passes through arrays: one to filter, and one to map.
A more efficient approach would be:
textFields.flatMap{ $0.text?.isEmpty == false ? $0.text! : nil }
The closure { $0.text?.isEmpty == false ? $0.text! : nil } returns the text property of each element if it is non-nil (since it is an optional property) and it is not empty (empty means == ""). Otherwise, it will return nil.
Because the flatMap method will already exclude any nil values from the final result, this guarantees that the return will be only the array of non-nil, non-empty text strings from the original elements, and does so with only a single pass through the array.

textFields.flatMap({ $0.text }).filter({ !$0.isEmpty })
Or, if you want to avoid flatMap:
textFields.map({ $0.text }).filter({ !($0?.isEmpty ?? true) })

This is how I would do it:
textFields.flatMap{
guard let t = $0.text, !t.isEmpty else { return nil }
return t
}

As Daniel Hall answer, you should avoid 2 passes.
Before Swift 4 you need to use flatMap as he said.
But now you need to use compactMap so:
let array = [1, 2, nil, 4, nil, 6]
let cleanArray = array.compactMap { $0 * 1 }
This runs 6 times (1 for each value) but the result only have 4 values.
More info here:
https://medium.com/#abhimuralidharan/higher-order-functions-in-swift-filter-map-reduce-flatmap-1837646a63e8

I solved it like this:
textFields.filter({$0.text! != ""}).map({$0.text!})

For filtering the non empty values, WE Can go for FILTER in SWIFT 3.
filter({$0.text! != ""})
you don't need to go for MAP function.
This will give you only non empty (!="") values.
Thanks.

Related

Return value of iteration over array

I'm trying to build a music library.
My main problem is when I iterate over the array with the #each method the return value is huge knowing that my array is all the albums and songs about one artist.
Would you know a way to iterate over arrays with a return value of nil or at least way shorter than the entire artist array I created?
Just return nil after the iteration if you don't want the collection as a return value.
def your_method
your_collection.each do |item|
# do something
end
nil
end
Compact method will eliminate nil values, if that is what you mean.
['foo', nil, 'bar'].compact.each do |part|
puts part
end
=> foo
bar
Add a && nil after your expression:
myarray.each {....} && nil

Swift Array Map Skip Value

Say I have the following code.
let myArray = [1,4,5,8,9,13,14,15]
let numbers = [4,8,13,15]
let finalArray = myArray.map({id in numbers.first(where: {$0 == id})!})
But I get an error because sometimes numbers.first(where: {$0 == id}) returns nil sometimes and can't be unwrapped.
I know this isn't the best example but it's the simplest example to explain what I'm trying to do.
My goal in this example is to have finalArray be [4,8,13,15]. So IF numbers.first(where: {$0 == id}) is nil just skip that value.
Is this possible with map in Swift? Or does the returned array length have to equal the array we are running map on?
Quick note. My example is very simple and in practice my problem is more complicated. I have my reasons for wanting to use map because it is an easy way to get a new array based on another array.
To expand on Rashwan L's answer, I believe the Swift 4 version is as follows:
let finalArray = myArray.compactMap({ id in numbers.first(where: {$0 == id}) })
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}) })

Filter Swift Array and Give Indices (Not Values)

I need to filter an array by some condition on its elements, but I need to obtain the indices of the elements that passed the test, not the elements themselves.
For example: given an array of Bool, I want to transform that into an array of Int that contains only the indices of the elements of the orginal array that are true.
I could do this:
// INPUT array:
let flags = [true, false, true, false]
// OUTPUT array:
var trueIndices = [Int]()
for (index, value) in flags.enumerated() where value == true {
trueIndices.append(index)
}
...but it isn't "swifty" at all.
Is there a more elegant way? Something akin to filter(), but that returns the indices instead of the elements.
You can directly filter the indices
let flags = [true, false, true, false]
let trueIndices = flags.indices.filter{ flags[$0] }
and
let falseIndices = flags.indices.filter{ !flags[$0] }
Not sure how much "swiftier" it is, but you can also use a consecutive filter and map operation or just a single flatMap instead. For sure, you can declare the array immutable and you don't have to write any explicit loops, both of which are usually considered a more functional approach than your current one.
let trueIndices = flags.enumerated().filter{$0.element}.map{$0.offset}
print(trueIndices)
Output:
[0,2]
Using a single flatMap:
let trueIndices = flags.enumerated().flatMap { (offset, flag) in flag ? offset : nil }
Here's what I would do. It's generalizable to sequences, or even collections whose subscripting isn't O(1).
let indicesOfTrue = Array(flags.enumerated().lazy.filter{ $0.element }.map{ $0.offset })

How do I apply my function to non-nil elements only?

I’m using Rails 4.2.7. I have this concise function that trims (strips) strings in my array
data_cols = data_cols.collect{|x| x.strip || x }
However if one of the elements in the array is nil, the above falls apart. How can I modify the above so that it won’t apply to nil elements?
If you want to remove nil values from the array entirely, you can use compact:
cols.compact.map { |col| col.strip }
# or
cols.compact.map(&:strip)
If you want to keep all values, but only operate on the non-nil values, you can simply use a nil-guard:
# straightforward, no magic, works across rubies:
cols.map { |col| col && col.strip }
# a wee dose of magic in ruby 2.3 (safe navigation operator)
cols.map { |col| col&.strip }
Finally, if you want to be more idiomatic and ruby-esque, you can use duck-typing and let the objects sort out themselves whether or not they can be "strip'ped":
cols.map { |col| col.respond_to?(:strip) ? col.strip : col }
you can use try
data_cols = data_cols.collect{|x| x.try(:strip) }
http://apidock.com/rails/Object/try
Depends if you still want the nil values in your result array. If not, you can just use compact:
data_cols = data_cols.compact.collect{|x| x.strip || x }
compact will remove all the nil values in the array.
If you want to keep nil values, you need to change your condition:
data_cols = data_cols.compact.collect{|x| x ? x.strip : nil }

In Ruby, how do I return all the index value of elements in an array that have the same string values in them?

How do I return all the index value of elements in an array that have the same string values in them?
For example:
myarray=[tree, bean, bean, bunny, frog, bean, soup]
If I searched for "bean" using something myarray.index(bean) it would return 1. If I did the same search using myarray.rindex(bean) it would return 5.
I need the method to myarray.{does this method exist?}(bean) that would return [1, 2, 5].
Any suggestions?
You can use #each_with_index (create pair of string, index), then #map (iterate over the pairs and if it matches, then return index, otherwise return nil) and finally #compact (remove nil values) the array.
myarray.each_with_index.map{|x,i| x == "bean"? i : nil}.compact
Slightly simpler soulution and a better one also in terms of efficiency would be probably this one with #each_index.
myarray.each_index.select{|x| myarray[x] == "bean"}
Btw. you should name the variable my_array, not myarray.
Simple & easy to understand solution given below
myarray = [:tree, :bean, :bean, :bunny, :frog, :bean, :soup]
result = []
search_for = :bean
myarray.each_with_index() { |element, index| result << index if element == search_for }
p result
Output
[1, 2, 5]

Resources