Ruby array intersection returning a blank array - arrays

I'm very new to Ruby so please go easy on me. I have this small function that doesn't want to perform an intersection command. If I go into irb and enter the arrays then set the intersection command like: third_array = array1 & array2, third_array returns the common element. But when I run this snippet through irb, it just returns [ ]. Any suggestions?
class String
define_method(:antigrams) do |word2|
array1 = []
array2 = []
array1.push(self.split(""))
array2.push(word2.split(""))
third_array = array1 & array2
third_array
end
end

After looking at what you have, I think your code boils down to this:
class String
def antigrams(word)
self.chars & word.chars
end
end
"flurry".antigrams("flagrant")
# => ["f", "l", "r"]
If you're calling split('') on a word that's effectively the same as chars, though a lot less efficient. Another mistake was pushing a whole array into an array, which creates a nested array of the form [ [ 'f', 'l', ... ] ]. Since the two resulting array-of-arrays have nothing in common, their inner arrays are different, the & operation returns an empty array.
What you meant was to concatenate the one array to the other, something that can be done with += for example.
Whenever you're curious what's happening, either use irb to try out chunks of code, or p to debug at different points in your method.

Related

Using flatten! on an array of arrays not working

I am building a script that takes in a column from a CSV that can contain 0 or more ID numbers. I have created an array of the column, however, since some cells have no ID number and some have multiple,I have an array of arrays.
I want to create an array where each element is a single ID (i.e split the IDs from each element in the array to a single element).
Here is my code so far:
require 'csv'
class MetadataTherapyParser
def initialize (csv)
#csv = csv
end
def parse_csv
therapy_array = []
CSV.foreach(#csv) do |csv_row|
therapy_array << csv_row[0]
end
therapy_array
end
def parse_therapies(therapy_array)
parsed_therapy_array = therapy_array.flatten!
end
end
metadata_parse = MetadataTherapyParser.new ("my_path.csv")
therapy_array = metadata_parse.parse_csv
metadata_parse.parse_therapies(therapy_array)
p therapy_array
However, the output is still an array of arrays. I am thinking it may have something to do with nil values? I have tried looking for answers online to no avail.
If you could give me some advice as how to fix this problem, it would be greatly appreciated!
Thank you in advance.
EDIT
I have posted a snippet of my output below. It still appears to be a nested array.
[nil, nil, "57e923a0f5c3c85c9200052b, 58b828f4f5c3c806490046a6", "57e923a0f5c3c85c9200052b, 4ffaf15af758862fb10155e3, 58b828f4f5c3c806490046a6", "57e923a0f5c3c85c9200052b, 4ffaf15af758862fb10155e3, 58b828f4f5c3c806490046a6", nil, nil, nil, nil, nil, "5f9176e50cf19216d6da9289", "6082f6bd0cf19225863fc985", "6082f6fd0cf192258d3fce0e", "6082f69e0cf19225ac3fc551", "6082f6a60cf19225a23fd3e4, 6082f6d30cf192258d3fce0a, 6082f7fa0cf19225953fc77c"]
You say you have "an array of arrays" but your example array is "57e923a0f5c3c85c9200052b, 4ffaf15af758862fb10155e3, 58b828f4f5c3c806490046a6" ... that's not an array. That's a string. You probably want to split strings that have commas into separate array elements.
So instead of
therapy_array << csv_row[0]
try instead
therapy_array << csv_row[0].to_s.split(',').map(&:strip)
the flatten is working perfectly. the issue you are having is that you have lots of strings in your output, with commas in them.
having not got a copy of your CSV, I'm going to assume that it has been parsed correctly, and that you do want to keep the contents of the first cell as it is:
def parse_therapies(therapy_array)
parsed_therapy_array = therapy_array.map { |x| x && x.split(/,/) }.flatten.compact
therapy_array.replace(parsed_therapy_array)
end
this will also remove all the nil elements, assuming you don't want them, using the compact procedure.

How to collapse a multi-dimensional array of hashes in Ruby?

Background:
Hey all, I am experimenting with external APIs and am trying to pull in all of the followers of a User from a site and apply some sorting.
I have refactored a lot of the code, HOWEVER, there is one part that is giving me a really tough time. I am convinced there is an easier way to implement this than what I have included and would be really grateful on any tips to do this in a much more eloquent way.
My goal is simple. I want to collapse an array of arrays of hashes (I hope that is the correct way to explain it) into one array of hashes.
Problem Description:
I have an array named f_collectionswhich has 5 elements. Each element is an array of size 200. Each sub-element of these arrays is a hash of about 10 key-value pairs. My best representation of this is as follows:
f_collections = [ collection1, collection2, ..., collection5 ]
collection1 = [ hash1, hash2, ..., hash200]
hash1 = { user_id: 1, user_name: "bob", ...}
I am trying to collapse this multi-dimensional array into one array of hashes. Since there are five collection arrays, this means the results array would have 1000 elements - all of which would be hashes.
followers = [hash1, hash2, ..., hash1000]
Code (i.e. my attempt which I do not want to keep):
I have gotten this to work with a very ugly piece of code (see below), with nested if statements, blocks, for loops, etc... This thing is a nightmare to read and I have tried my hardest to research ways to do this in a simpler way, I just cannot figure out how. I have tried flatten but it doesn't seem to work.
I am mostly just including this code to show I have tried very hard to solve this problem, and while yes I solved it, there must be a better way!
Note: I have simplified some variables to integers in the code below to make it more readable.
for n in 1..5 do
if n < 5
(0..199).each do |j|
if n == 1
nj = j
else
nj = (n - 1) * 200 + j
end
#followers[nj] = #f_collections[n-1].collection[j]
end
else
(0..199).each do |jj|
njj = (4) * 200 + jj
#followers[njj] = #f_collections[n-1].collection[jj]
end
end
end
Oh... so It is not an array objects that hold collections of hashes. Kind of. Lets give it another try:
flat = f_collection.map do |col|
col.collection
end.flatten
which can be shortened (and is more performant) to:
flat = f_collection.flat_map do |col|
col.collection
end
This works because the items in the f_collection array are objects that have a collection attribute, which in turn is an array.
So it is "array of things that have an array that contains hashes"
Old Answer follows below. I leave it here for documentation purpose. It was based on the assumption that the data structure is an array of array of hashes.
Just use #flatten (or #flatten! if you want this to be "inline")
flat = f_collections.flatten
Example
sub1 = [{a: 1}, {a: 2}]
sub2 = [{a: 3}, {a: 4}]
collection = [sub1, sub2]
flat = collection.flatten # returns a new collection
puts flat #> [{:a=>1}, {:a=>2}, {:a=>3}, {:a=>4}]
# or use the "inplace"/"destructive" version
collection.flatten! # modifies existing collection
puts collection #> [{:a=>1}, {:a=>2}, {:a=>3}, {:a=>4}]
Some recommendations for your existing code:
Do not use for n in 1..5, use Ruby-Style enumeration:
["some", "values"].each do |value|
puts value
end
Like this you do not need to hardcode the length (5) of the array (did not realize you removed the variables that specify these magic numbers). If you you want to detect the last iteration you can use each_with_index:
a = ["some", "home", "rome"]
a.each_with_index do |value, index|
if index == a.length - 1
puts "Last value is #{value}"
else
puts "Values before last: #{value}"
end
end
While #flatten will solve your problem you might want to see how DIY-solution could look like:
def flatten_recursive(collection, target = [])
collection.each do |item|
if item.is_a?(Array)
flatten_recursive(item, target)
else
target << item
end
end
target
end
Or an iterative solution (that is limited to two levels):
def flatten_iterative(collection)
target = []
collection.each do |sub|
sub.each do |item|
target << item
end
end
target
end

Return only the first and last items of an array in Ruby

I need to create a method named first_and_last. It should use one argument, an Array, and return a new Array with only the first and last objects of the argument.
Here is what I have so far, but the method seems to ignore the initial argument and just create a new array.
def first_and_last(a)
arr = Array.new
arr = ["a", "b", "c"]
end
You can use the values_at() method to get the first and last elements in an array like this:
def first_and_last(input_array)
input_array.values_at(0,-1)
end
Depending on what behavior you're looking for, it might not work on arrays with 1 or 0 elements. You can read more about the method here.
You can also use .first for the first element in the array, and .last for the last element in an array.
def first_and_last(arr)
[arr.first, arr.last]
end
p first_and_last([1,2,3,4,5,6])
Or....
def first_and_last(arr)
[arr[0], arr[-1]]
end
p first_and_last([1,2,3,4,5,6])

how to divide nested array using RDD in spark

I am trying to divide a nested array using RDD in Spark. For example, there is a TextFile that contains four sentences, like this:
"he is good", "she is good", "I am good", "we are good"
I used val arr = sc.textFile("filename").map(_.split(" ")) command and got this:
Array[Array[String]] = Array(Array(he, is, good),
Array(she, is, good),
... )
I want to use each array elements (i.e. Array(he, is, good)) but I don't know how to divide this. How can I divide this?
It is unclear what you mean by 'divided', but typically in functional programming languages, when you want to do anything with each element of a collection (or 'iterable'), you can use the map function. map converts each element based on a function you passed it. For example, in a worksheet you can do this:
val sentences = Array(Array("he", "is", "good"),
Array("she", "is", "very", "good"))
def yodaize(sentence: Array[String]): Array[String] = {
val reversed = sentence.reverse
println("Yoda says, '%s'".format(reversed.mkString(" ")))
reversed
}
yodaize(Array("I", "am", "small"))
val yodaSentences = sentences.map(yodaize)
The function yodaize does two things: it reverses the sentence passed to it and as a side effect prints out the reversed sentence. The worksheet output of the above is:
sentences: Array[Array[String]] = [[Ljava.lang.String;#faffecf
yodaize: yodaize[](val sentence: Array[String]) => Array[String]
Yoda says, 'small am I'
res0: Array[String] = [Ljava.lang.String;#4bf1c779
Yoda says, 'good is he'
Yoda says, 'good very is she'
yodaSentences: Array[Array[String]] = [[Ljava.lang.String;#40a19a85
It's hard to see directly here, but yodaSentences is the original array with each sub-array reversed:
Array(Array("good", "is", "he"),
Array("good", "very", "is", "she"))
With map you can pass in any function. It can directly convert the element or have a side effect. In this manner functional languages can deal with each element without really ever needing to 'divide' them. Note that other functions like flatMap, foldLeft and filter can be used to perform other sorts of permutations on your collection.
You can use map and foreach both for Arrays and RDDs:
yourReference.map(arrayOfStrings => arrayOfStrings.mkString)
or
yourReference.foreach(arrayOfStrings => println(Arrays.toString(arrayOfStrings)))

Adding items to a new array with index

Trying to make a method skip_animals that takes an animals array and a skip integer and returns an array of all elements except first skip number of items.
input: skip_animals(['leopard', 'bear', 'fox', 'wolf'], 2)
expected output: ["2:fox", "3:wolf"]
def skip_animals(animals, skip)
arr = Array.new
animals.each_with_index{|animal, index| arr.push("#{animal}:#{index}") }
puts arr.drop(skip)
end
This instead puts each output on a separate line and doesn't add them to the array arr. I thought the arr.push would add them correctly. What do I have to do to get the elements added to the array?
I want to use these methods, not map or something more advanced. I need to tinker with this each_with_index line, not overhaul it.
(This is a challenge on Hackerrank, so it uses STDIN and STDOUT)
EDIT
Here is my updated code with p instead of puts. It's giving me a weird output of two different arrays, not sure why.
def skip_animals(animals, skip)
arr = Array.new
animals.each_with_index{|animal, index| arr.push("#{index}:#{animal}") }
p arr.drop(skip)
end
This gives me two lines of output:
["3:panda", "4:tiger", "5:deer"]
["0:leopard", "1:bear", "2:fox", "3:wolf", "4:dog", "5:cat"]
I'm assuming the top is the correct array, but I don't get why the second is printing also, or why it has a different set of animals.
Use p instead of puts.
irb(main):001:0> puts ['1', '2']
1
2
=> nil
irb(main):002:0> p ['1', '2']
["1", "2"]
According to the documentation, puts:
Writes the given objects to ios as with IO#print. Writes a record
separator (typically a newline) after any that do not already end with
a newline sequence. If called with an array argument, writes each
element on a new line. If called without arguments, outputs a single
record separator.
BTW, I would code like this (using Enumerable#map + returning result instead of printing inside the function):
def skip_animals(animals, skip)
animals.drop(skip).each_with_index.map { |animal, index|
("#{index + skip}:#{animal}")
}
end
p skip_animals(['leopard', 'bear', 'fox', 'wolf'], 2)
just remove puts remove form this line puts arr.drop(skip)
def skip_animals(animals, skip)
arr = Array.new
animals.each_with_index{|animal, index| arr.push("#{animal}:#{index}") }
arr.drop(skip)
end

Resources