So I'm currently trying to sort values from a file. I'm stuck on the finding the first attribute, and am not sure why. I'm new to regex and ruby so I'm not sure how to go about the problem. I'm trying to find values of a,b,c,d,e where they are all positive numbers.
Here's what the line will look like
length=<a> begin=(<b>,<c>) end=(<d>,<e>)
Here's what I'm using to find the values
current_line = file.gets
if current_line == nil then return end
while current_line = file.gets do
if line =~ /length=<(\d+)> begin=((\d+),(\d+)) end=((\d+),(\d+))/
length, begin_x, begin_y, end_x, end_y = $1, $2, $3, $4, $5
puts("length:" + length.to_s + " begin:" + begin_x.to_s + "," + begin_y.to_s + " end:" + end_x.to_s + "," + end_y.to_s)
end
end
for some reason it never prints anything out, so I'm assuming it never finds a match
Sample input
length=4 begin=(0,0) end=(3,0)
A line with 0-4 decimals after 2 integers seperated by commas.
So it could be any of these:
2 4 1.3434324,3.543243,4.525324
1 2
18 3.3213,9.3233,1.12231,2.5435
7 9 2.2,1.899990
0 3 2.323
Here is your regex:
r = /length=<(\d+)> begin=((\d+),(\d+)) end=((\d+),(\d+))/
str.scan(r)
#=> nil
First, we need to escape the parenthesis:
r = /length=<(\d+)> begin=\((\d+),(\d+)\) end=\((\d+),(\d+)\)/
Next, add the missing < and > after "begin" and "end".
r = /length=<(\d+)> begin=\(<(\d+)>,<(\d+)>\) end=\(<(\d+)>,<(\d+)>\)/
Now let's try it:
str = "length=<4779> begin=(<21>,<47>) end=(<356>,<17>)"
but first, let's set the mood
str.scan(r)
#=> [["4779", "21", "47", "356", "17"]]
Success!
Lastly (though probably not necessary), we might replace the single spaces with \s+, which permits one or more spaces:
r = /length=<(\d+)>\s+begin=\(<(\d+)>,<(\d+)>\)\send=\(<(\d+)>,<(\d+)>\)/
Addendum
The OP has asked how this would be modified if some of the numeric values were floats. I do not understand precisely what has been requested, but the following could be modified as required. I've assumed all the numbers are non-negative. I've also illustrated one way to "build" a regex, using Regexp#new.
s1 = '<(\d+(?:\.\d+)?)>' # note single parens
#=> "<(\\d+(?:\\.\\d+)?)>"
s2 = "=\\(#{s1},#{s1}\\)"
#=> "=\\(<(\\d+(?:\\.\\d+)?)>,<(\\d+(?:\\.\\d+)?)>\\)"
r = Regexp.new("length=#{s1} begin#{s2} end#{s2}")
#=> /length=<(\d+(?:\.\d+)?)> begin=\(<(\d+(?:\.\d+)?)>,<(\d+(?:\.\d+)?)>\) end=\(<(\d+(?:\.\d+)?)>,<(\d+(?:\.\d+)?)>\)/
str = "length=<47.79> begin=(<21>,<4.7>) end=(<0.356>,<17.999>)"
str.scan(r)
#=> [["47.79", "21", "4.7", "0.356", "17.999"]]
Sample input:
length=4 begin=(0,0) end=(3,0)
data.txt:
length=3 begin=(0,0) end=(3,0)
length=4 begin=(0,1) end=(0,5)
length=2 begin=(1,3) end=(1,5)
Try this:
require 'pp'
Line = Struct.new(
:length,
:begin_x,
:begin_y,
:end_x,
:end_y,
)
lines = []
IO.foreach('data.txt') do |line|
numbers = []
line.scan(/\d+/) do |match|
numbers << match.to_i
end
lines << Line.new(*numbers)
end
pp lines
puts lines[-1].begin_x
--output:--
[#<struct Line length=3, begin_x=0, begin_y=0, end_x=3, end_y=0>,
#<struct Line length=4, begin_x=0, begin_y=1, end_x=0, end_y=5>,
#<struct Line length=2, begin_x=1, begin_y=3, end_x=1, end_y=5>]
1
With this data.txt:
2 4 1.3434324,3.543243,4.525324
1 2
18 3.3213,9.3233,1.12231,2.5435
7 9 2.2,1.899990
0 3 2.323
Try this:
require 'pp'
data = []
IO.foreach('data.txt') do |line|
pieces = line.split
csv_numbers = pieces[-1]
next if not csv_numbers.index('.') #skip the case where there are no floats on a line
floats = csv_numbers.split(',')
data << floats.map(&:to_f)
end
pp data
--output:--
[[1.3434324, 3.543243, 4.525324],
[3.3213, 9.3233, 1.12231, 2.5435],
[2.2, 1.89999],
[2.323]]
Related
I am new to programming in LUA. And I am not able to solve this question below.
Given a number N, generate a star pattern such that on the first line there are N stars and on the subsequent lines the number of stars decreases by 1.
The pattern generated should have N rows. In every row, every fifth star (*) is replaced with a hash (#). Every row should have the required number of stars (*) and hash (#) symbols.
Sample input and output, where the first line is the number of test cases
This is what I tried.. And I am not able to move further
function generatePattern()
n = tonumber(io.read())
i = n
while(i >= 1)
do
j = 1
while(j<=i)
do
if(j<=i)
then
if(j%5 == 0)
then
print("#");
else
print("*");
end
print(" ");
end
j = j+1;
end
print("\n");
i = i-1;
end
end
tc = tonumber(io.read())
for i=1,tc
do
generatePattern()
end
First, just the stars without hashes. This part is easy:
local function pattern(n)
for i=n,1,-1 do
print(string.rep("*", i))
end
end
To replace each 5th asterisk with a hash, you can extend the expression with the following substitution:
local function pattern(n)
for i=n,1,-1 do
print((string.rep("*", i):gsub("(%*%*%*%*)%*", "%1#")))
end
end
The asterisks in the pattern need to be escaped with a %, since * holds special meaning within Lua patterns.
Note that string.gsub returns 2 values, but they can be truncated to one value by adding an extra set of parentheses, leading to the somewhat awkward-looking form print((..)).
Depending on Lua version the metamethod __index holding rep for repeats...
--- Lua 5.3
n=10
asterisk='*'
print(asterisk:rep(n))
-- puts out: **********
#! /usr/bin/env lua
for n = arg[1], 1, -1 do
local char = ''
while #char < n do
if #char %5 == 4 then char = char ..'#'
else char = char ..'*'
end -- mod 5
end -- #char
print( char )
end -- arg[1]
chmod +x asterisk.lua
./asterisk.lua 15
Please do not follow this answer since it is bad coding style! I would delete it but SO won't let me. See comment and other answers for better solutions.
My Lua print adds newlines to each printout, therefore I concatenate each character in a string and print the concatenated string out afterwards.
function generatePattern()
n = tonumber(io.read())
i = n
while(i >= 1)
do
ouput = ""
j = 1
while(j<=i)
do
if(j%5 == 0)
then
ouput=ouput .. "#";
else
ouput=ouput .. "*";
end
j = j+1;
end
print(ouput);
i = i-1;
end
end
Also this code is just yours minimal transformed to give the correct output. There are plenty of different ways to solve the task, some are faster or more intuitive than others.
Here's what I expect. I have a string with numbers that need to be changed into letters (a kind of cipher) and spaces to move into different letter, and there is a tripple spaces that represent a space in output. For example, a string "394 29 44 44 141 6" will be decrypted into "Hell No".
function string.decrypt(self)
local output = ""
for i in self:gmatch("%S+") do
for j, k in pairs(CODE) do
output = output .. (i == j and k or "")
end
end
return output
end
Even though it decrypts the numbers correctly I doesn't work with spacebars. So the string I used above decrypts into "HellNo", instead of expected "Hell No". How can I fix this?
You can use
CODE = {["394"] = "H", ["29"] = "e", ["44"] = "l", ["141"] = "N", ["6"] = "o"}
function replace(match)
local ret = nil
for i, v in pairs(CODE) do
if i == match then
ret = v
end
end
return ret
end
function decrypt(s)
return s:gsub("(%d+)%s?", replace):gsub(" ", " ")
end
print (decrypt("394 29 44 44 141 6"))
Output will contain Hell No. See the Lua demo online.
Here, (%d+)%s? in s:gsub("(%d+)%s?", replace) matches and captures one or more digits and just matches an optional whitespace (with %s?) and the captured value is passed to the replace function, where it is mapped to the char value in CODE. Then, all double spaces are replaced with a single space with gsub(" ", " ").
Say I have the multi-lines text:
str = [[
The lazy dog sleeping on the yard.
While a lazy old man smoking.
The yard never green again.
]]
I can split each words using:
for w in str:gmatch("%S+") do print(w) end
But how I can get results as an example:
The = 3 words, line 1,3
Lazy = 2 words, line 1,2
Dog = 1 word, line 1
..and so on?
Thank you
You could detect the \n using gmatch like you are already to count the words.
The pattern would be something like "[^\n]+" and the code something like this:
local str = [[
The lazy dog sleeping on the yard.
While a lazy old man smoking.
The yard never green again.
]]
local words = {}
local lines = {}
local line_count = 0
for l in str:gmatch("[^\n]+") do
line_count = line_count + 1
for w in l:gmatch("[^%s%p]+") do
w = w:lower()
words[w] = words[w] and words[w] + 1 or 1
lines[w] = lines[w] or {}
if lines[w][#lines[w]] ~= line_count then
lines[w][#lines[w] + 1] = line_count
end
end
end
for w, count in pairs(words) do
local the_lines = ""
for _,line in ipairs(lines[w]) do
the_lines = the_lines .. line .. ','
end
--The = 3 words, line 1,3
print(w .." = " .. count .. " words , lines " .. the_lines)
end
Full output, note i also changed the pattern you used for capturing the words to "[^%s%p]+" i did this to remove the . that was getting attached to smoking, again, and yard.
smoking = 1 words , lines 2,
while = 1 words , lines 2,
green = 1 words , lines 3,
never = 1 words , lines 3,
on = 1 words , lines 1,
lazy = 2 words , lines 1,2,
the = 3 words , lines 1,3,
again = 1 words , lines 3,
man = 1 words , lines 2,
yard = 2 words , lines 1,3,
dog = 1 words , lines 1,
old = 1 words , lines 2,
a = 1 words , lines 2,
sleeping = 1 words , lines 1,
I'm using Ruby 2.4 and Rails 5. I have an array of indexes within a line
[5, 8, 10]
How do I take the above array, and a string, and form anotehr array of strings that are split by the above indexes? FOr instance, if the string is
abcdefghijklmn
and split it based ont eh above indexes, I would have an array with the following strings
abcde
fgh
ij
klmn
Try this
str = "abcdefghijklmn"
positions = [5, 8, 10]
parts = [0, *positions, str.size].each_cons(2).map { |a, b| str[a...b] }
# => ["abcde", "fgh", "ij", "klmn"]
Or,
If the positions are constant and known ahead of runtime (for example if they were the format for a phone number or credit card) just use a regexp
str.match(/(.....)(...)(..)(.*)/).captures
# => ["abcde", "fgh", "ij", "klmn"]
This will get the Job done
str = "abcdefghijklmn"
arr_1 = [5, 8, 10]
arr_2, prev = [], 0
(arr_1.length + 1).times do |x|
if arr_1[x] == nil then arr_1[x] = str.size end
arr_2 << str[prev..arr_1[x] -1]
prev = arr_1[x]
end
p arr_2
---------------------------------------
Program Run Output
["abcde", "fgh", "ij", "klmn"]
---------------------------------------
I hope this Helps
This question already has answers here:
Finding all possible combinations of numbers to reach a given sum
(32 answers)
Closed 6 years ago.
Need to create an array whose sum should be equal to expected value.
inp = [1,2,3,4,5,6,7,8,9,10]
sum = 200
output:
out = [10,10,9,1,3,3,3,7,.....] whose sum should be 200
or
out = [10,7,3,....] Repeated values can be used
or
out = [2,3,4,9,2,....]
I tried as,
arr = [5,10,15,20,30]
ee = []
max = 200
while (ee.sum < max) do
ee << arr.sample(1).first
end
ee.pop(2)
val = max - ee.sum
pair = arr.uniq.combination(2).detect { |a, b| a + b == val }
ee << pair
ee.flatten
Is there any effective way to do it.
inp = [1,2,3,4,5,6,7,8,9,10]
sum = 20
inp.length.downto(1).flat_map do |i|
inp.combination(i).to_a # take all subarrays of length `i`
end.select do |a|
a.inject(:+) == sum # select only those summing to `sum`
end
One might take a random element of resulting array.
result = inp.length.downto(1).flat_map do |i|
inp.combination(i).to_a # take all subarrays of length `i`
end.select do |a|
a.inject(:+) == sum # select only those summing to `sum`
end
puts result.length
#⇒ 31
puts result.sample
#⇒ [2, 4, 5, 9]
puts result.sample
#⇒ [1, 2, 3, 6, 8]
...
Please note, that this approach is not efficient for long-length inputs. As well, if any original array’s member might be taken many times, combination above should be changed to permutation, but this solution is too ineffective to be used with permutation.
I found an answer of this question in the following link:
Finding all possible combinations of numbers to reach a given sum
def subset_sum(numbers, target, partial=[])
s = partial.inject 0, :+
#check if the partial sum is equals to target
puts "sum(#{partial})=#{target}" if s == target
return if s >= target #if we reach the number why bother to continue
(0..(numbers.length - 1)).each do |i|
n = numbers[i]
remaining = numbers.drop(i+1)
subset_sum(remaining, target, partial + [n])
end
end
subset_sum([1,2,3,4,5,6,7,8,9,10],20)