count the distance between to strings in an array Ruby - arrays

i have an array
line_one = ["flinders street", "richmond", "east richmond", "burnley", "hawthorn", "glenferrie"]
user_input1 = "flinders street"
user_input2 = "glenferrie"
how could I count the distance between the two strings?
expected output 5.

The first thing that comes to mind:
line_one = ["flinders street", "richmond", "east richmond", "burnley", "hawthorn", "glenferrie"]
user_input1 = "flinders street"
user_input2 = "glenferrie"
(line_one.find_index(user_input1) - line_one.find_index(user_input2)).abs
#=> 5

line_one = ["flinders street", "richmond", "east richmond", "burnley", "hawthorn", "glenferrie"]
Code
p (line_one.index("flinders street")...line_one.index("glenferrie")).count
output
5

Related

How to batch enumerables in ruby

In my quest to understand ruby's enumerable, I have something similar to the following
FileReader.read(very_big_file)
.lazy
.flat_map {|line| get_array_of_similar_words } # array.size is ~10
.each_slice(100) # wait for 100 items
.map{|array| process_100_items}
As much as each flat_map call emits an array of ~10 items, I was expecting the each_slice call to batch the items in 100's but that is not the case. I.e wait until there are 100 items before passing them to the final .map call.
How do I achieve functionality similar to the buffer function in reactive programming?
To see how lazy affects the calculations, let's look at an example. First construct a file:
str =<<~_
Now is the
time for all
good Ruby coders
to come to
the aid of
their bowling
team
_
fname = 't'
File.write(fname, str)
#=> 82
and specify the slice size:
slice_size = 4
Now I will read lines, one-by-one, split the lines into words, remove duplicate words and then append those words to an array. As soon as the array contains at least 4 words I will take the first four and map them into the longest word of the 4. The code to do that follows. To show how the calculations progress I will salt the code with puts statements. Note that IO::foreach without a block returns an enumerator.
IO.foreach(fname).
lazy.
tap { |o| puts "o1 = #{o}" }.
flat_map { |line|
puts "line = #{line}"
puts "line.split.uniq = #{line.split.uniq} "
line.split.uniq }.
tap { |o| puts "o2 = #{o}" }.
each_slice(slice_size).
tap { |o| puts "o3 = #{o}" }.
map { |arr|
puts "arr = #{arr}, arr.max = #{arr.max_by(&:size)}"
arr.max_by(&:size) }.
tap { |o| puts "o3 = #{o}" }.
to_a
#=> ["time", "good", "coders", "bowling", "team"]
The following is displayed:
o1 = #<Enumerator::Lazy:0x00005992b1ab6970>
o2 = #<Enumerator::Lazy:0x00005992b1ab6880>
o3 = #<Enumerator::Lazy:0x00005992b1ab6678>
o3 = #<Enumerator::Lazy:0x00005992b1ab6420>
line = Now is the
line.split.uniq = ["Now", "is", "the"]
line = time for all
line.split.uniq = ["time", "for", "all"]
arr = ["Now", "is", "the", "time"], arr.max = time
line = good Ruby coders
line.split.uniq = ["good", "Ruby", "coders"]
arr = ["for", "all", "good", "Ruby"], arr.max = good
line = to come to
line.split.uniq = ["to", "come"]
line = the aid of
line.split.uniq = ["the", "aid", "of"]
arr = ["coders", "to", "come", "the"], arr.max = coders
line = their bowling
line.split.uniq = ["their", "bowling"]
arr = ["aid", "of", "their", "bowling"], arr.max = bowling
line = team
line.split.uniq = ["team"]
arr = ["team"], arr.max = team
If the line lazy. is removed the return value is the same but the following is displayed (.to_a at the end now being superfluous):
o1 = #<Enumerator:0x00005992b1a438f8>
line = Now is the
line.split.uniq = ["Now", "is", "the"]
line = time for all
line.split.uniq = ["time", "for", "all"]
line = good Ruby coders
line.split.uniq = ["good", "Ruby", "coders"]
line = to come to
line.split.uniq = ["to", "come"]
line = the aid of
line.split.uniq = ["the", "aid", "of"]
line = their bowling
line.split.uniq = ["their", "bowling"]
line = team
line.split.uniq = ["team"]
o2 = ["Now", "is", "the", "time", "for", "all", "good", "Ruby",
"coders", "to", "come", "the", "aid", "of", "their",
"bowling", "team"]
o3 = #<Enumerator:0x00005992b1a41a08>
arr = ["Now", "is", "the", "time"], arr.max = time
arr = ["for", "all", "good", "Ruby"], arr.max = good
arr = ["coders", "to", "come", "the"], arr.max = coders
arr = ["aid", "of", "their", "bowling"], arr.max = bowling
arr = ["team"], arr.max = team
o3 = ["time", "good", "coders", "bowling", "team"]

R. Array-based replacement of string matches in data frame

I have a data frame column containing sentences.
Within these sentences, there's the whole host of words which I want to remove.
These are words that could appear more than once in a single sentence, and when found I want to remove these words entirely.
e.g.
Sample list of words for removal: ("the", "and", "a") * (list will have 100's of words)
String Before: "the quick brown fox jumps over the lazy dog and cat"
String After: "quick brown fox jumps over lazy dog cat"
sentences <- as.data.frame(c("it's a new sentence","another sentence i've constructed","and a third sentence"))
colnames(sentences) <- c("sentence")
stop_words <- list( "i" = '', "a" = "", "me" = '' , "my" = "", "myself" = "", "we" = "", "it's" = "", "a" = "", "i've" = "")
stop_pattern <- paste0("\\b", "(", paste0(stop_words, collapse = "|"),")","\\b")
trimws(gsub("\\s{2}", " ", gsub(stop_pattern, "", sentences$sentence)))
Output should remove words such as "I've" from the above sentences, however fails to do so.
Output is as shows:
[1] "it's a new sentence" "another sentence i've constructed" "and a third sentence"
Try:
stop_pattern <- paste0("\\b", "(", paste0(stop_words, collapse = "|"),")","\\b") trimws(gsub("\\s{2}", " ", gsub(stop_pattern, "", sentences)))

Swift how to read through all indexes of array using for loop with index [i]

The Code I have so far only does for 1 index however I want it to read all existing indexes within the array. element array can carry many groups of numbers for example
Array ["2,2,5" , "5,2,1"] contains 2 indexes [0] and [1]
var element = Array[0]
let splitData = element.components(separatedBy: ",")
// split data will always contain 3 values.
var value1 = splitData[0]
var value2 = splitData[1]
var value3 = splitData[2]
print("value 1 is : " + value1 + " value 2 is : " + value2 + " value 3 is: " + value3)
the output of this code when Array ["2,2,5" , "5,2,1"] is :
value 1 is : 2 value 2 is : 2 value 3 is : 5
As the output suggests how can i iterate through all indexes of Array to display each of their 3 values.
I want the output to be :
value 1 is : 2 value 2 is : 2 value 3 is : 5
value 1 is : 5 value 2 is : 2 value 3 is : 1
I believe I need to use a for loop however I am unsure how to apply it to this. I am quite new to coding. Any help will be Appreciated
for i in 0..<array.count {
var element = array[i]
let splitData = element.components(separatedBy: ",")
// split data will always contain 3 values.
var value1 = splitData[0]
var value2 = splitData[1]
var value3 = splitData[2]
print("value 1 is : " + value1 + " value 2 is : " + value2 + " value 3 is: " + value3)
}
here are two solutions you can use, depending on what is the best result for you.
1) If your goal is to transform an array like ["3,4,5", "5,6", "1", "4,9,0"] into a flattened version ["3", "4", "5", "5", "6", "1", "4", "9", "0"] you can do it easily with the flatMap operator in the following way:
let myArray = ["3,4,5", "5,6", "1", "4,9,0"]
let flattenedArray = myArray.flatMap { $0.components(separatedBy: ",") }
Then you can iterate on it like every other array,
for (index, element) in myArray.enumerated() {
print("value \(index) is: \(element)")
}
2) If you just want to iterate over it and keep the levels you can use the following code.
let myArray = ["3,4,5", "5,6", "1", "4,9,0"]
for elementsSeparatedByCommas in myArray {
let elements = elementsSeparatedByCommas.components(separatedBy: ",")
print(elements.enumerated().map { "value \($0) is: \($1)" }.joined(separator: " "))
}
Hope that helps!

Performing union of two arrays with custom rules

I have two arrays
b = ["John Roberts", "William Koleva", "Lili Joe", "Victoria Jane", "Allen Thomas"]
a = ["Jon Roberts", "Wil Koleva", "Lilian Joe", "Vic Jane", "Al Thomas"]
Currently I am using the union operator on these two arrays, like this: a | b. When combined, even though the names in each array are the "same" name (they're just using the shortened version of the name), it will duplicate my names.
My proposed solution to this is simply choose the first occurrence of first initial + last name as the name to perform the union on, however, I don't recall there being any methods in Ruby that can perform such an operation.
So the result of some_method(a | b) will return c which is just:
["John Roberts", "William Koleva", "Lili Joe", "Victoria Jane", "Allen Thomas"]
I am wondering how I could go about achieving this?
b = ["John Roberts", "William Koleva", "Lili Joe", "Victoria Jane", "Allen Thomas"]
a = ["Jon Roberts", "Wil Koleva", "Lilian Joe", "Vic Jane", "Al Thomas"]
r = /
\s # match a space
[[:alpha:]]+ # match > 0 alphabetic characters
\z # match end of string
/x # free-spacing regex definition mode
(b+a).uniq { |str| [str[0], str[r]] }
#=> ["John Roberts", "William Koleva", "Lili Joe", "Victoria Jane", "Allen Thomas"]
This uses the form of the method Array#uniq that employs a block.
You may alternatively write (b|a).uniq { |str| [str[0], str[r]] }
The steps are as follows.
c = b+a
# => ["John Roberts", "William Koleva", "Lili Joe", "Victoria Jane", "Allen Thomas",
# "Jon Roberts", "Wil Koleva", "Lilian Joe", "Vic Jane", "Al Thomas"]
The first element of c passed to the block is
str = c.first
#=> "John Roberts"
so the block calculation is
[str[0], str[r]]
#=> ["J", " Roberts"]
The calculations are similar for all the other elements of c. The upshot is that
c.uniq { |str| [str[0], str[r]] }
is equivalent to selecting the first elements of c, when converted to [<first name initial>, <last name>], that match an element of the array d, where
d = [["J", "Roberts"], ["W", "Koleva"], ["L", "Joe"], ["V", "Jane"], ["A", "Thomas"],
["J", "Roberts"], ["W", "Koleva"], ["L", "Joe"], ["V", "Jane"], ["A", "Thomas"]].uniq
#=> [["J", "Roberts"], ["W", "Koleva"], ["L", "Joe"], ["V", "Jane"], ["A", "Thomas"]]
Pascal suggested that it would be better for uniq's block to return a string:
{ |str| "#{str[0]} #{str[r]}" }
(e.g., "J Roberts") which might instead be written
{ |str| str.sub(/(?<=.)\S+/,"") }
The inclusion of the space after the first initial is optional (e.g., "JRoberts" would also work).
Sure, just use Enumerable#uniq with a block:
c = (a | b).uniq do |full_name|
first_name, last_name = full_name.split(nil, 2)
[first_name[0], last_name]
end
Note: the first iteration of the code used the initials instead of abbreviated name.
Perhaps you can introduce the concept of a Name? It's a bit more code than just providing a block to uniq but it nicely encapsulates everything related.
class Name
def initialize(first, last)
#first, #last = first, last
end
def abbreviated
"#{#first[0]} #{#last}"
end
def eql?(other)
return false if !other.respond_to?(:abbreviated)
abbreviated == other.abbreviated
end
def hash
abbreviated.hash
end
def full
"#{#first} #{#last}"
end
end
a = Name.new('John', 'Roberts')
b = Name.new('Jon', 'Roberts')
c = Name.new('William', 'Koleva')
d = Name.new('Wil', 'Koleva')
x = [a, c]
y = [b, d]
p (y | x).map(&:full)
It's worth noting that abbreviated firstname does not really suffice to check equality of names.
Consider:
Jim Jackson
James Jackson
Janine Jackson
...

How to join two array attributes if the second is != "" in Ruby

I have an array with two elements below:
if params["location"]
params["location"]["street"] =[
params["location"].delete("address1"),
params["location"].delete("address2")
].compact.join(", ")
l = ::Location.create!(street: params["location"]["street"],
city: params["location"]["city"],
state: params["location"]["state"],
zip: params["location"]["postal"],
country: params["location"]["country"])
What I am trying to do is join the two together sperated by a ", " only if address2 is an empty string/nil.
Example 1:
address1 = "56 West Gay Street"
address2 = "Apt. 211"
Actual: "56 West Gay Street, Apt.211"
Expected: "56 West Gay Street, Apt.211"
Example 2:
address1 = "56 West Gay Street"
address2 = ""
Actual: "56 West Gay Street, "
Expected: "56 West Gay Street"
The problem is that params['location']['address2'] is not empty, but an empty string. You can use present? to select only strings that are not blank.
if params['location']
street = [
params['location'].delete('address1'),
params['location'].delete('address2')
].select(&:present?).join(', ')
l = ::Location.create!(params['location'].merge('street' => street))
end
Assuming a and b are your two string:
[a,b].select(&:present?).join(", ")

Resources