I have a string called "example", like this:
192.168.1.40,8.8.8.8,12.34.45.56,408,-,1812
192.168.1.128,192.168.101.222,12.34.45.56,384,-,1807
and I would like to obtain this output:
{"string1":"192.168.1.40","string2":"8.8.8.8",“string3":“12.34.45.56”,“string4”:408,“string5”:“-”,"string6":1812}
{"string1":"192.168.1.128","string2":"192.168.101.222",“string3":“12.34.45.56”,“string4”:384,“string5”:“-”,"string6":1807}
I did this:
example = example.gsub("\n","}\n{\"string1\": \"")
example = example.insert(0, "{\"string1\": \"")
example = example.concat("}")
and I obtained:
{"string1":"192.168.1.40,8.8.8.8,12.34.45.56,408,-,1812}
{"string1":"192.168.1.128,192.168.101.222,12.34.45.56,384,-,1807}
but I don't know how can I do the others changes. Thanks!!
Well, to get it as a ruby hash, which you can output as json or whatever you need:
out = {}
your_input_data.split(",").each_with_index { |val, i| out["string#{i}"] = val }
(but you would need to do this for each line: input.lines.each { |line| ... do the above here } - but I am not clear - do you want a list of maps?)
I made the assumption that you didn't want values that were just numbers to be double-quoted.
DATA.each_line do |line|
l = line.chomp.split(',').map.with_index do |v, i|
v = v =~ /^\d+$/ ? v : "\"#{v}\""
"\"string#{i+1}\":#{v}"
end
print "{", l.join(','), "}\n"
end
__END__
192.168.1.40,8.8.8.8,12.34.45.56,408,-,1812
192.168.1.128,192.168.101.222,12.34.45.56,384,-,1807
Result:
{"string1":"192.168.1.40","string2":"8.8.8.8","string3":"12.34.45.56","string4":408,"string5":"-","string6":1812}
{"string1":"192.168.1.128","string2":"192.168.101.222","string3":"12.34.45.56","string4":384,"string5":"-","string6":1807}
It seems from the code you wrote that you are looking for a single string as output rather than a more elaborate Ruby data structure or output to a printed stream.
This is working for me:
example = '192.168.1.40,8.8.8.8,12.34.45.56,408,-,1812
192.168.1.128,192.168.101.222,12.34.45.56,384,-,1807'
result = example.split("\n").map do |line|
n = 0
line.split(',').map{|s| %Q|"string#{n+=1}":"#{s}"|}.join(',')
end.map{|c| "{#{c}}"}.join("\n")
puts result
{"string1":"192.168.1.40","string2":"8.8.8.8","string3":"12.34.45.56","string4":"408","string5":"-","string6":"1812"}
{"string1":" 192.168.1.128","string2":"192.168.101.222","string3":"12.34.45.56","string4":"384","string5":"-","string6":"1807"}
This splits into lines then splits each line into separate strings, then concatenates each string with its JSON key and finally reassembles with join first with commas and then with newline. If you'd rathet have lists than reassembled strings, just omit the respective join.
Related
I was recently asked this in an interview and was figuring out a way to do this without using regex in Ruby as I was told it would be a bonus if you can solve it without using regex.
Question: Assume that the hash has 1 million key, value pairs and we have to be able to sub the variables in the string that are between % % this pattern. How would I be able to do this without regex.
We have a string str = "%greet%! Hi there, %var_1% that can be any other %var_2% injected to the %var_3%. Nice!, goodbye)"
we have a hash called dict = { greet: 'Hi there', var_1: 'FIRST VARIABLE', var_2: 'values', var_3: 'string', }
This was my solution:
def template(str, dict)
vars = value.scan(/%(.*?)%/).flatten
vars.each do |var|
value = value.gsub("%#{var}%", dict[var.to_sym])
end
value
end
There are many ways to solve this, but you will probably need some kind of parsing and / or lexical analysis if you don't want to use built-in pattern matching.
Let's keep it very simple and say that your string's content falls into two categories: text and variable which are separated by %, e.g. (you could also think of the variables being enclosed by %, but that's harder to implement)
str = "Hello %name%, hope to see you %when%!"
# TTTTTT VVVV TTTTTTTTTTTTTTTTTT VVVV T
As you can see, the categories are alternating. We can utilize this and write a little helper method that turns a string into a list of [type, value] pairs, something like this:
def each_part(str)
return enum_for(__method__, str) unless block_given?
type = [:text, :var].cycle
buf = ''
str.each_char do |char|
if char != '%'
buf << char
else
yield type.next, buf
buf = ''
end
end
yield type.next, buf
end
It starts by defining an enumerator that will cycle between the two types and an empty buffer. It will then read each_char from the string. If the char is not %, it will just append it to the buffer and keep reading. Once it encounters a %, it will yield the current buffer along with the type and start a new buffer (next will also switch the type). After the loop ends, it will yield once more to output the remaining characters.
It outputs this kind of data:
each_part(str).to_a
#=> [[:text, "Hello "],
# [:var, "name"],
# [:text, ", hope to see you "],
# [:var, "when"],
# [:text, "!"]]
We can use this to convert the string:
dict = { name: 'Tom', when: 'soon' }
output = ''
each_part(str) do |type, value|
case type
when :text
output << value
when :var
output << dict[value.to_sym]
end
end
p output
#=> "Hello Tom, hope to see you soon!"
You could of course combine parsing and evaluation, but I like the separation. An full-fledged parser might involve even more steps.
A very simple approach:
First, split the string on '%':
str = "%greet%! Hi there, %var_1% that can be any other %var_2% injected to the %var_3%. Nice!, goodbye)"
chunks = str.split('%')
Now we can assume given the way the problem has been specified, that every other "chunk" will be a key to replace. Iterating with the index will make that easier to figure out.
chunks.each_with_index { |c, i| chunks[i] = (i.even? ? c : dict[c.to_sym]) }.join
Result:
"Hi there! Hi there, FIRST VARIABLE that can be any other values injected to the string. Nice!, goodbye)"
Note: this does not handle malformed input well at all.
Here is my code and output below, i would like to have it so that instead of saying how many occurences, it would output the number of times the letter appears in asterisk form.
For exmaple if "a" appeared four times within a sentence the output would produce:
"a": ****
the_file='C:\Users\Jack\Documents\Ruby\Lab1\lyric.txt'
h = Hash.new
f = File.open(the_file, "r")
f.each_line { |line|
words = line.split(//)
words.each { |w|
if h.has_key?(w)
h[w] = h[w] + 1
else
h[w] = 1
end
}
}
# sort the hash by value, and then print it in this sorted order
h.sort{|a,b| a[1]<=>b[1]}.each { |elem|
puts "\"#{elem[0]}\" : #{elem[1]} occurrences"
}
Screenshot of my current program and output
Instead of #{elem[1]} occurences you just need to write #{'*' * elem[1]}
See method description for more details.
I would like to show another possible alternative way to achieve the word count.
Letting apart the file reading, let's consider the following string:
line = 'Here is my code and output below, i would like to have it so that instead of saying how many occurrences, it would output the number of times the letter appears in asterisk form.'
h = Hash.new(0)
line.downcase.each_char{ |ch| h[ch] += 1 if ('a'..'z').include? ch }
h.to_a.sort_by(&:last).reverse.each { |ch, count| puts "#{ch}: " + "*" * count}
Initialise the hash with default = 0 allow you to start the count without checking if key exists: Hash#default.
Iterate over the line by String#each_char
I counted only case insensitive letters, up to you
For sorting change the Hash into an Array with Hash#to_a
For printing the histogram, as shown in other posts
puts "\"#{elem[0]}\": " + '*' * elem[1]
+ for concatenate, string * number is to repeat certain string number times.
With strings in Ruby, you can use math operators against them. So, you know how many times the letter appears (in elem[1]). In that case you can just multiply the asterisks symbol by that amount:
"\"#{elem[0]}: #{'*' * elem[1]}\""
I'm attempting to write a function that takes a string and returns it with all vowels removed. Below is my code.
def vowel(str)
result = ""
new = str.split(" ")
i = 0
while i < new.length
if new[i] == "a"
i = i + 1
elsif new[i] != "a"
result = new[i] + result
end
i = i + 1
end
return result
end
When I run the code, it returns the exact string that I entered for (str). For example, if I enter "apple", it returns "apple".
This was my original code. It had the same result.
def vowel(str)
result = ""
new = str.split(" ")
i = 0
while i < new.length
if new[i] != "a"
result = new[i] + result
end
i = i + 1
end
return result
end
I need to know what I am doing wrong using this methodology. What am I doing wrong?
Finding the bug
Let's see what's wrong with your original code by executing your method's code in IRB:
$ irb
irb(main):001:0> str = "apple"
#=> "apple"
irb(main):002:0> new = str.split(" ")
#=> ["apple"]
Bingo! ["apple"] is not the expected result. What does the documentation for String#split say?
split(pattern=$;, [limit]) → anArray
Divides str into substrings based on a delimiter, returning an array of these substrings.
If pattern is a String, then its contents are used as the delimiter when splitting str. If pattern is a single space, str is split on whitespace, with leading whitespace and runs of contiguous whitespace characters ignored.
Our pattern is a single space, so split returns an array of words. This is definitely not what we want. To get the desired result, i.e. an array of characters, we could pass an empty string as the pattern:
irb(main):003:0> new = str.split("")
#=> ["a", "p", "p", "l", "e"]
"split on empty string" feels a bit hacky and indeed there's another method that does exactly what we want: String#chars
chars → an_array
Returns an array of characters in str. This is a shorthand for str.each_char.to_a.
Let's give it a try:
irb(main):004:0> new = str.chars
#=> ["a", "p", "p", "l", "e"]
Perfect, just as advertised.
Another bug
With the new method in place, your code still doesn't return the expected result (I'm going to omit the IRB prompt from now on):
vowel("apple") #=> "elpp"
This is because
result = new[i] + result
prepends the character to the result string. To append it, we have to write
result = result + new[i]
Or even better, use the append method String#<<:
result << new[i]
Let's try it:
def vowel(str)
result = ""
new = str.chars
i = 0
while i < new.length
if new[i] != "a"
result << new[i]
end
i = i + 1
end
return result
end
vowel("apple") #=> "pple"
That looks good, "a" has been removed ("e" is still there, because you only check for "a").
Now for some refactoring.
Removing the explicit loop counter
Instead of a while loop with an explicit loop counter, it's more idiomatic to use something like Integer#times:
new.length.times do |i|
# ...
end
or Range#each:
(0...new.length).each do |i|
# ...
end
or Array#each_index:
new.each_index do |i|
# ...
end
Let's apply the latter:
def vowel(str)
result = ""
new = str.chars
new.each_index do |i|
if new[i] != "a"
result << new[i]
end
end
return result
end
Much better. We don't have to worry about initializing the loop counter (i = 0) or incrementing it (i = i + 1) any more.
Avoiding character indices
Instead of iterating over the character indices via each_index:
new.each_index do |i|
if new[i] != "a"
result << new[i]
end
end
we can iterate over the characters themselves using Array#each:
new.each do |char|
if char != "a"
result << char
end
end
Removing the character array
We don't even have to create the new character array. Remember the documentation for chars?
This is a shorthand for str.each_char.to_a.
String#each_char passes each character to the given block:
def vowel(str)
result = ""
str.each_char do |char|
if char != "a"
result << char
end
end
return result
end
The return keyword is optional. We could just write result instead of return result, because a method's return value is the last expression that was evaluated.
Removing the explicit string
Ruby even allows you to pass an object into the loop using Enumerator#with_object, thus eliminating the explicit result string:
def vowel(str)
str.each_char.with_object("") do |char, result|
if char != "a"
result << char
end
end
end
with_object passes "" into the block as result and returns it (after the characters have been appended within the block). It is also the last expression in the method, i.e. its return value.
You could also use if as a modifier, i.e.:
result << char if char != "a"
Alternatives
There are many different ways to remove characters from a string.
Another approach is to filter out the vowel characters using Enumerable#reject (it returns a new array containing the remaining characters) and then join the characters (see Nathan's answer for a version to remove all vowels):
def vowel(str)
str.each_char.reject { |char| char == "a" }.join
end
For basic operations like string manipulation however, Ruby usually already provides a method. Check out the other answers for built-in alternatives:
str.delete('aeiouAEIOU') as shown in Gagan Gami's answer
str.tr('aeiouAEIOU', '') as shown in Cary Swoveland's answer
str.gsub(/[aeiou]/i, '') as shown in Avinash Raj's answer
Naming things
Cary Swoveland pointed out that vowel is not the best name for your method. Choose the names for your methods, variables and classes carefully. It's desirable to have a short and succinct method name, but it should also communicate its intent.
vowel(str) obviously has something to do with vowels, but it's not clear what it is. Does it return a vowel or all vowels from str? Does it check whether str is a vowel or contains a vowel?
remove_vowels or delete_vowels would probably be a better choice.
Same for variables: new is an array of characters. Why not call it characters (or chars if space is an issue)?
Bottom line: read the fine manual and get to know your tools. Most of the time, an IRB session is all you need to debug your code.
I should use regex.
str.gsub(/[aeiou]/i, "")
> string= "This Is my sAmple tExt to removE vowels"
#=> "This Is my sAmple tExt to removE vowels"
> string.delete 'aeiouAEIOU'
#=> "Ths s my smpl txt t rmv vwls"
You can create a method like this:
def remove_vowel(str)
result = str.delete 'aeiouAEIOU'
return result
end
remove_vowel("Hello World, This is my sample text")
# output : "Hll Wrld, Ths s my smpl txt"
Live Demo
Assuming you're trying to learn about the basics of programming, rather than finding the quickest one-liner to do this (which would be to use a regular expression as Avinash has said), you have a number of problems with your code you need to change.
new = str.split(" ")
This line is likely the culprit, because it splits the string based on spaces. So your input string would have to be "a p p l e" to have the effect you're looking for.
new = str.split("")
You should also remove the duplicate i = i+1 once you've changed that.
As others have already identified the problems with the OP's code, I will merely suggest an alternative; namely, you could use String#tr:
"Now is the time for all good people...".tr('aeiouAEIOU', '')
#=> "Nw s th tm fr ll gd ppl..."
If regex is not allowed, you can do it this way:
def remove_vowels(string)
string.split("").delete_if { |letter| %w[a e i o u].include? letter }.join
end
I have the following array:
arr = ["lol","test"]
and a code:
matches = content.downcase.split & arr
where content is a string. This code returns ["lol"] when content = "Something lol", but fails to return anything when content = "Something #lol." with a comma or hashtag etc. It always fails when there is no exact match.
I'd like to match with the strings in the array as substring. How can this be done by adapting the above code?
Not efficient, but this works:
matches = content.downcase.split.select{|s| arr.any?{|_s| s.include?(_s)}}
Try splitting on non-word boundary as shown below
arr = ["lol","test"]
content = "Something #lol."
p matches = content.downcase.split(/\W/) & arr
Outputs
["lol"]
Sample runs from Try ruby is shown below
It's not obvious what kind of results you want to receive. I think you can use String#scan. It will return an array of matched data:
patterns = ["lol","test"]
data = content.downcase
matches = patterns.flat_map { |pattern| data.scan(pattern) }
s = content.downcase
arr.select { |w| s =~ /\b#{w}\b/ }
Is there any way to create the regex /func:\[sync\] displayPTS/ from string func:[sync] displayPTS?
The story behind this question is that I have serval string pattens to search against in a text file and I don't want to write the same thing again and again.
File.open($f).readlines.reject {|l| not l =~ /"#{string1}"/}
File.open($f).readlines.reject {|l| not l =~ /"#{string2}"/}
Instead , I want to have a function to do the job:
def filter string
#build the reg pattern from string
File.open($f).readlines.reject {|l| not l =~ pattern}
end
filter string1
filter string2
s = "func:[sync] displayPTS"
# => "func:[sync] displayPTS"
r = Regexp.new(s)
# => /func:[sync] displayPTS/
r = Regexp.new(Regexp.escape(s))
# => /func:\[sync\]\ displayPTS/
I like Bob's answer, but just to save the time on your keyboard:
string = 'func:\[sync] displayPTS'
/#{string}/
If the strings are just strings, you can combine them into one regular expression, like so:
targets = [
"string1",
"string2",
].collect do |s|
Regexp.escape(s)
end.join('|')
targets = Regexp.new(targets)
And then:
lines = File.readlines('/tmp/bar').reject do |line|
line !~ target
end
s !~ regexp is equivalent to not s =~ regexp, but easier to read.
Avoid using File.open without closing the file. The file will remain open until the discarded file object is garbage collected, which could be long enough that your program will run out of file handles. If you need to do more than just read the lines, then:
File.open(path) do |file|
# do stuff with file
end
Ruby will close the file at the end of the block.
You might also consider whether using find_all and a positive match would be easier to read than reject and a negative match. The fewer negatives the reader's mind has to go through, the clearer the code:
lines = File.readlines('/tmp/bar').find_all do |line|
line =~ target
end
How about using %r{}:
my_regex = "func:[sync] displayPTS"
File.open($f).readlines.reject { |l| not l =~ %r{#{my_regex}} }