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 }
Related
array = [[1555,100],[nil,95],[1774,nil],[1889,255]]
What would be the best way to remove the 2nd and 3rd elements from array since they have NULL fields?
Expected output :
array = [[1555,100],[1889,255]]
arr = [[1555,100],[nil,95],[1774,nil],[1889,255]]
arr.reject { |a,b| (a && b).nil? }
#=> [[1555, 100], [1889, 255]]
And yet another option:
array.reject { |a| a.any?(&:nil?) }
It is very similar to Cary Swoveland's answer, but will work with arrays of any length and will remove [false, nil] as well.
Use .compact to remove nil elements from array of arrays
array.map(&:compact)
# array = [[1555,100], [95], [1774], [1889, 255]]
Edit
Use .reject! to remove sub arrays containing nil elements.
array.reject! { |e| e.any? nil }
# array = [[1555,100], [1889,255]]
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
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 })
Using .flatten is a handy little trick to take an array of sub-arrays and turn it into a single array.
For example: [[1,3],2,[5,8]].flatten => [1,3,2,5,8]
You can even include nil [1,[2,nil],3].flatten will result in [1,2,nil,3].
This kind of method is very useful when nesting a .map method, but how would you account for an empty sub-array? For example: [1,[2,3],[],4].flatten would return [1,2,3,4]... but what if I need to keep track of the empty sub array maybe turn the result into [1,2,3,0,4] or [1,2,3,nil,4]
Is there any elegant way to do this? Or would I need to write some method to iterate through each individual sub-array and check it one by one?
If you don't need to recursively check nested sub-arrays:
[1,[2,3],[],4].map { |a| a == [] ? nil : a }.flatten
First map the empty arrays into nils, then flatten
[1,2,[1,2,3],[]].map{|x| if x.is_a? Array and x.empty? then nil else x end}.flatten
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.