Modify CSV rows before importing with tap and yield - ruby-on-rails

I am working on a feature on an open source project.
At some point a CSV file is imported and I create an array out of it to pass it in a new object.
After
CSV.open(path, 'rb', opts)
this is used to get the block of code
.tap { |c| yield c if block_given? }
My question is, how to get access to the data imported?
After tap there is a method sequence.
Edit:
I don't want to re-write it I want to test it. I know I am supposed to use tap to modify the data, but I don't know how to do that as I don't see any way to reference the data of the CSV

It does not really have anything to do with file manipulation at this point. It's more about understanding how #tap and blocks work.
def block
[1, 2, 3, 4, 5].tap { |array| yield array if block_given?}
end
block { |array| array.each{ |element| puts element } }
The output:
1
2
3
4
5
In a method block, an array [1, 2, 3, 4, 5] is initialized, afterwards we tap into that array, which makes that array available in the tap's block.
In the example above, when [1, 2, 3, 4, 5] is passed to tap it is available in array variable, which is specified like this: |array|. Then array is yield to the block if block_given?. That means that in the new block |array| contains [1, 2, 3, 4, 5] too. So in that new block that you pass to tap, you can modify that array, or just output it like I did.

Related

2 arrays insert via (column) double dimension array ruby (short method) [duplicate]

I am browsing through the ruby Array iterators. And I can't find what I am looking for and I think it already exists:
I have two arrays:
["a", "b", "c"]
[0,1,2]
And I want to merge as so:
[ [0, "a"], [1, "b"], [2, "c"] ]
I think the iterator exists in the standard library (I used it before) but I am having trouble finding its name.
This should work:
[0,1,2].zip(["a", "b", "c"]) # => [[0, "a"], [1, "b"], [2, "c"]]
From the official documentation of the Array#zip function:
Converts any arguments to arrays, then merges elements of self with corresponding elements from each argument.
This generates a sequence of ary.size n-element arrays, where n is one more than the count of arguments.
For more info and some other examples, refer to:
https://ruby-doc.org/core-2.4.2/Array.html#method-i-zip
You are looking for the zip function
https://apidock.com/ruby/Array/zip
I think you could use https://apidock.com/ruby/Enumerator/each_with_index
See this post difference between each.with_index and each_with_index in Ruby?
or if you have specific values and you want to map them you would use map or zip. It explains it well in this post
Combine two Arrays into Hash

Rail postgres array contains (<#) dont include nil

I have a Matchups table that includes a field that is equipment_id and stores an array
I am trying to search the Matchups table to get any records that have an array where it contains specific equipment ids. For example:
So I am running:
search_equipment_ids = [1, 2, 3, 4, 5]
Matchup.where("equipment_id <# ARRAY[?]", search_equipment_ids)
The following are a few examples of arrays that are stored in the table in equipment_id and what the above query would result in
[1, 2] = True
[3, 5] = True
[1, 2, 3] = True
[1, 2, nil, 3] = False
How can get the search to ignore that there might be a nil included in the array. I am going to make a change to how the array is stored so from here on out there will be no nil values...but in the mean time I need to take these into account.
I am using: https://www.postgresql.org/docs/9.5/static/functions-array.html as a reference.
I dont want to use the && operator because the Matchups table is huge it will return to many results if just comparing if one equipment id is present. I want to only return if all the equipment_ids are contained (excluding any nils).
Thank you!
How about removing the nil values from the array? That's easy:
Matchup.where(..., equipment_ids.compact)

How can I delete certain indeces defined in an array from an other array in ruby

I have a method that iterates over an array, does a bunch of stuff under certain conditions and depending on these conditions I would ALSO like to delete some of the elements. In order to keep track of the indexes I like to delete I converted the each in to an each_with_index loop and store the index of the elements that I like to delete in an array index_array. How can I delete exactly the items on those indexes in my original array? Looping over the index_array and using delete_at would change the original index. Please see below description of the scenario:
> my_array = [0,1,2,3,4,5,6,7]
> delete_these_indexes = [1,2,5]
the desired result is:
> my_array => [0,3,4,6,7,8]
How about this?
my_array = [0, 1, 2, 3, 4, 5, 6, 7]
delete_these_indices = [1, 2, 5]
delete_these_indices.sort.reverse_each {|i| my_array.delete_at(i) }
p my_array
# => [0, 3, 4, 6, 7, 8]
It's important to delete from the end of the array, since deleting an item will change the indices of all subsequent items, ergo sort.reverse_each. If you know the array is already sorted, you can just do reverse_each.
If you don't care bout modifying the delete_these_indices array, you can be somewhat more terse:
delete_these_indices.sort!
my_array.delete_at(i) while (i = delete_these_indices.pop)
Again, you can skip sort! if you know delete_these_indices is already sorted.
keep_these_indexes = my_array.each_index.to_a - delete_these_indexes
#=> [0, 3, 4, 6, 7]
If you wish to modify my_array (which appears to be the case):
my_array.replace(my_array.values_at(*keep_these_indexes))
#=> [0, 3, 4, 6, 7]
If not:
new_array = my_array.values_at(*keep_these_indexes)
See Array#values_at.
delete_these_indexes.each_with_index do |val, i|
my_array.delete_at(val - i)
end
deletes at the desired index taking into account how many have previously been deleted and adjusting for that
https://repl.it/CeHZ
Probably not the best answer, but you can do this as well:
delete_at_indices.each {|ind| my_array[ind] = nil }
my_array.delete(nil)
Takes a first pass to conceptually invalidate the data at the specified indices, then the call to .delete will blow out any values that match what's passed in.
This solution assumes that you can define a value that isn't valid for your array. Using nil will be problematic if you're treating this as a sparsely populated array (where nil is a valid value)
Technically you're iterating through each array once, but that Gentleman's Agreement on what your deletable value might make some people uncomfortable

Removing a "subset" from an array in Ruby

I want to remove some elements from an array contained in another array.
The way to do this in Java is:
myArray.removeAll(anotherArray)
This code removes the elements contained in anotherArray from myArray.
Is there something like removing the elements contained in myArray INTERSECTION anotherArray from myArray?
Yes, this is what the - operator (really Array#- method) is for:
a = [1, 2]
b = [2]
a - b
# => [1]
Use "Array Difference"
There's more than one way to do this in Ruby, but the most common is to call the array difference method Array#-. The documentation for this method says:
Returns a new array that is a copy of the original array, removing any items that also appear in other_ary. The order is preserved from the original array.
It compares elements using their hash and eql? methods for efficiency.
[ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ] #=> [ 3, 3, 5 ]
If you need set-like behavior, see the library class Set.
The minus sign looks like an operator, but is actually a method call on the first Array object. Obviously, you could also look at Set#difference if you're working with Set objects rather than an Array.

Tag "metadata" onto an array in ruby

I have some code which assembles two-dimensional arrays to be sent to the Spreadsheet gem's excel-building methods. I'd like to "tag" some subarrays (corresponding to rows) with formatting codes like {:color => "red"}.
Can anyone see a way of doing this? I could achieve a similar result by storing a seperate object which has the formatting option and (for example) the indexes to all rows i want to apply that format to. But it would be nicer if I could stick it straight onto the row itself as i build my data.
One thing that occurred to me is to use some kind of namespaced hash as the last entry in an array, if i want to format it, and then strip that out again in the spreadsheet builder. But, this seems risky as it's then in the array's actual data. Is there any kind of instance variable or something i can tap into with an array to "shove" my metadata in there?
I'm using Rails 2.2 for this app, in case that's relevant.
Since each row contains data and possibly some metadata, it seems natural to represent all rows as an Array of Hashes, where each Hash contains a :data key and a :metadata key. The latter can be omitted or can just point to an empty Hash if you do not have any metadata for the row, whichever you prefer.
I have no experience with the Spreadsheet gem, but I assume it requires an Array of Arrays as input, which you can create in a straightforward manner from the Array of Hashes as shown in the code below.
rows = [
{
data: [2, 3, 5],
metadata: { color: 'red' }
},
{
data: [7, 11, 13],
metadata: {}
}
]
# Transform into Array of Arrays, removing all metadata
rows.map { |row| row[:data] }
# => [[2, 3, 5], [7, 11, 13]]

Resources