set table random value from table - random

I have the following code:
wrg = { "1.png", "2.png", "3.png", "4.png" };
table = { }
for i = 1, 4 do
table[ i ] = wrg[ math.random( 1, #wrg ) ]
end
for i = 1, 4 do
print( table[ i ] )
end
output:
4.png
2.png
4.png
4.png
I don't need repeat "4.png" how fix?

You need a random permutation. For instance, this code:
wrg = { "1.png", "2.png", "3.png", "4.png" };
t = {}
n=#wrg
for i=1,n do
t[i]=wrg[i]
end
math.randomseed(os.time())
for i=1,n-1 do
local j=math.random(i,n)
t[i],t[j]=t[j],t[i]
end
for i=1,n do
print(t[i])
end

Related

Hash tree depth Ruby

I'm supposed to write a method that takes a nested hash as input and returns that hash with added "depth" keys. So, for example, the following input:
tree = {
a: 1,
b: 2,
c: { d: { e: 3 } }
}
would yield the following return value:
{
a: 1,
b: 2,
c: {
d: {
e: 3,
depth: 2
},
depth: 1
},
depth: 0
}
If the input is not a hash, then the function should return nil.
This is what I came up with:
def depth(hash)
num = 0
hash.each do |key, value|
if value.class == Hash
num += 1
v[:depth] = num
value.each do |k, v|
if v.class == Hash
num += 1
v[:depth] = num
v.each do |ky, val|
if val.class == Hash
num += 1
v[:depth] = num
val.each do |ke, vl|
if vl.class == Hash
num += 1
v[:depth] = num
end
end
end
end
end
end
end
num = 0
end
end
but it's limited to hash depth of 4, and I can't just keep making the method bigger.
Try this.
def depth(h, i=0)
h.each_with_object(depth: i) { |(k,v),g| g[k] = v.is_a?(Hash) ? depth(v, i+1) : v }
end
depth { a: 1, b: 2, c: { d: { e: 3 } }
#=> {:depth=>0, :a=>1, :b=>2, :c=>{:depth=>1, :d=>{:depth=>2, :e=>3}}}

I'm getting an unexpected keyword error for "elsif"

def letter_case_count(string)
char = new Hash
char[:lower] = 0
char[:upper] = 0
char[:neither] = 0
string.split("").each do |x|
if ('A'..'Z').include?(x)
char[:upper]++
elsif ('a'..'z').include?(x)
char[:lower]++
else
char[:neither]++
end
end
end
puts letter_case_count('abCdef 123') == { lowercase: 5, uppercase: 1, neither: 4 }
puts letter_case_count('AbCd +Ef') == { lowercase: 3, uppercase: 3, neither: 2 }
puts letter_case_count('123') == { lowercase: 0, uppercase: 0, neither: 3 }
puts letter_case_count('') == { lowercase: 0, uppercase: 0, neither: 0 }
I get this error.
(repl):9: syntax error, unexpected keyword_elsif
elsif ('a'..'z').include?(x)
^
(repl):11: syntax error, unexpected keyword_else
(repl):13: syntax error, unexpected keyword_end
(repl):20: syntax error, unexpected end-of-input, expecting keyword_end
...: 0, uppercase: 0, neither: 0 }
...
^
There are a bunch of unexpected keywords and unexpected end of inputs. Not sure why, I haven't programmed in Ruby in almost a yearn and I can't see what the problem is.
Ruby doesn't have pre-increment or post-increment operators. Use +=1 and it should work.
def letter_case_count(string)
char = {}
char[:lower] = 0
char[:upper] = 0
char[:neither] = 0
string.split('').each do |x|
if ('A'..'Z').include?(x)
char[:upper] += 1
elsif ('a'..'z').include?(x)
char[:lower] += 1
else
char[:neither] += 1
end
end
char #returning the char is also important.
end
Using different keys for comparison will also return wrong results, it should be..
puts letter_case_count('abCdef 123') == { lower: 5, upper: 1, neither: 4 }
puts letter_case_count('AbCd +Ef') == { lower: 3, upper: 3, neither: 2 }
puts letter_case_count('123') == { lower: 0, upper: 0, neither: 3 }
puts letter_case_count('') == { lower: 0, upper: 0, neither: 0 }
Better Approach:
def letter_case_count(string)
{
lower: string.scan(/[a-z]/).count,
upper: string.scan(/[A-Z]/).count,
neither: string.scan(/[^a-z]/i).count
}
end
There are bunch of syntax errors in your code.
def letter_case_count(string)
char = Hash.new # not new Hash
char[:lower] = 0
char[:upper] = 0
char[:neither] = 0
string.split("").each do |x|
if ('A'..'Z').include?(x)
char[:upper]+=1 # var++ is not valid ruby code
elsif ('a'..'z').include?(x)
char[:lower]+=1 #same here
else
char[:neither]+=1 # same here
end
end
end
puts letter_case_count('abCdef 123') == { lowercase: 5, uppercase: 1, neither: 4 }
puts letter_case_count('AbCd +Ef') == { lowercase: 3, uppercase: 3, neither: 2 }
puts letter_case_count('123') == { lowercase: 0, uppercase: 0, neither: 3 }
puts letter_case_count('') == { lowercase: 0, uppercase: 0, neither: 0 }
UPDATE
For that kind of tasks, use Ruby's minitest from stdlib.
All in one file example (all of them will fail)
require 'minitest/autorun'
class String
def letter_case_count
char = Hash.new # not new Hash
char[:lower] = 0
char[:upper] = 0
char[:neither] = 0
self.split("").each do |x|
if ('A'..'Z').include?(x)
char[:upper]+=1 # var++ is not valid ruby code
elsif ('a'..'z').include?(x)
char[:lower]+=1 #same here
else
char[:neither]+=1 # same here
end
end
return char
end
end
class TestFoo < MiniTest::Test
def setup
#w1, #w2, #w3, #w4 = ["abCdef 123", "AbCd +Ef", "123", ""].map {|e| String.new(e)}
end
def test_some
assert_equal #w1.letter_case_count, { lowercase: 3, uppercase: 1, neither: 4 }
end
def test_some_other
assert_equal #w2.letter_case_count, { lowercase: 3, uppercase: 3, neither: 2 }
end
def test_other
assert_equal #w3.letter_case_count, { lowercase: 0, uppercase: 0, neither: 3 }
end
def test_definitely_other
assert_equal #w4.letter_case_count, { lowercase: 0, uppercase: 0, neither: 0 }
end
end

Conway's Game of Life having logic issues

I'm working on Conway's Game of Life. I have some logic issue that it always finishes in 2 or 3 ticks (usually only 2). Most of the time, it is with all cells dead, but occasionally it will have a 1 or 2 still alive. I can't find what part is causing the behavior of the second tick being almost entirely, if not completely, dead.
Do any of you see any major issues that might be causing this behavior?
require 'pry'
class Game
def initialize(size)
#size = size
#ticks = 1
#current_board = Array.new(size) { Array.new(size) { (rand(99) % 5 == 0) ? false : true } }
#future_board = Array.new(size) { Array.new(size) }
# #future_board = #current_board
end
def is_alive?(y, x)
if #current_board[y]
#current_board[y][x]
end
end
def check_neigbors(y, x)
neighbors = {
top: [y-1, x],
top_right: [y-1, x+1],
right: [y, x+1],
bottom_right: [y+1, x+1],
bottom: [y+1, x],
bottom_left: [y+1, x-1],
left: [y, x-1],
top_left: [y-1, x-1]
}
neighbor_state = {
top: false,
top_right: false,
right: false,
bottom_right: false,
bottom: false,
bottom_left: false,
left: false,
top_left: false
}
# binding.pry
neighbors.each { |k, v| neighbor_state[k] = true if is_alive?(v[0], v[1]) }
live_neighbor_count = 0
neighbor_state.each_value { |v| live_neighbor_count += 1 if v }
live_neighbor_count
end
def cell_lives(y, x)
#future_board[y][x] = true
end
def cell_dies(y, x)
#future_board[y][x] = false
end
def display_board
# need to display board here
system("clear")
# #current_board.each do
# |r| puts r.map { |c| c ? 'O' : 'X' }.join(" ")
# |r| puts r.map { |c| c }.join(" ")
puts #current_board.map { |row| row.map { |cell| cell ? 'X' : ' ' }.inspect }
# end
puts "\nTicks: #{#ticks}"
end
def play
loop do
display_board
#current_board.each do |r| # outer loop to iterate through rows
row_index = #current_board.index(r).to_i
r.each do |c| # inner loop to iterate through columns
column_index = r.index(c).to_i
live_neighbor_count = check_neigbors(row_index, column_index) # count the number of live neighbors
cell_dies(row_index, column_index) if ( is_alive?(row_index, column_index) ) && live_neighbor_count < 2 # rule 1
cell_lives(row_index, column_index) if ( is_alive?(row_index, column_index) ) && ( live_neighbor_count == 2 || live_neighbor_count == 3 ) # rule 2
cell_dies(row_index, column_index) if ( is_alive?(row_index, column_index) ) && live_neighbor_count > 3 # rule 3
cell_lives(row_index, column_index) if !( is_alive?(row_index, column_index) ) && live_neighbor_count == 3 # rule 4
end
end
if #current_board == #future_board # board is gridlocked. Game over!
puts "\nGAME OVER!"
exit!
else
#current_board = #future_board # update the board
#ticks += 1
sleep(1)
end
end
end
end
print "How large of a grid do you want: "
grid_size = gets.to_i
game = Game.new grid_size
game.play
In Conway's game of life, if you start with a random arrangement of cells, most of them do die on the second tick just because of the rules of the game, so I have no evidence there is a bug.
How about you make a glider or something and make sure it behaves as expected? If it doesn't work, then please post the output of your program and point out the first frame where the output is wrong, and post what you would expect the output to be.
try :-
#current_board = #futureboard.dup

Return the elements present exactly once in an array

I am a beginner in Ruby. Can anyone help me to write code for this, please?
Given an Array, return the elements that are present exactly once in the array.
For example, it should pass the following test cases:
returns [1,4,5], given [1,2,2,3,3,4,5]
returns [1,3], given [1,2,2,3,4,4]
Put the items in an array. a = [1,2,2,3,4,4] Then run a few filters to get the items you want.
a.group_by { |x| x }.reject { |k,v| v.count > 1 }.keys
#=> [1,3]
Updated With Stefan's keys suggestion.
a = [1,2,2,3,3,4,5]
p a.select{|i| a.count(i) == 1}
# >> [1, 4, 5]
a = [1,2,2,3,4,4]
p a.select{|i| a.count(i) == 1}
# >> [1, 3]
Benchmarks
require 'benchmark'
a = [1,2,2,3,3,4,5]
n = 1000000
Benchmark.bm(15) do |x|
x.report('priti') { n.times { a.select{|i| a.count(i) == 1} } }
x.report('Jason') { n.times { a.group_by { |x| x }.reject { |k,v| v.count > 1 }.keys } }
x.report('rogerdpack2') { n.times {
bad = {}
good = {}
a.each{|v|
if bad.key? v
# do nothing
else
if good.key? v
bad[v] = true
good.delete(v)
else
good[v] = true;
end
end
}
good.keys
}
}
end
with this result
priti 3.152000 0.000000 3.152000 ( 3.247000)
Jason 4.633000 0.000000 4.633000 ( 4.845000)
rogerdpack2 3.853000 0.000000 3.853000 ( 3.886000)
and with a larger array:
require 'benchmark'
a = [1,2,2,3,3,4,5]*5 + [33,34]
n = 1000000
Benchmark.bm(15) do |x|
x.report('priti') { n.times { a.select{|i| a.count(i) == 1} } }
x.report('Jason') { n.times { a.group_by { |x| x }.reject { |k,v| v.count > 1 }.keys } }
x.report('rogerdpack2') { n.times {
bad = {}
good = {}
a.each{|v|
if bad.key? v
# do nothing
else
if good.key? v
bad[v] = true
good.delete(v)
else
good[v] = true;
end
end
}
good.keys
}
}
x.report('priti2') { n.times { a.uniq.select{|i| a.count(i) == 1} }}
end
you get result:
user system total real
priti 60.435000 0.000000 60.435000 ( 60.769151)
Jason 10.827000 0.016000 10.843000 ( 10.978195)
rogerdpack2 9.141000 0.000000 9.141000 ( 9.213843)
priti2 15.897000 0.000000 15.897000 ( 16.007201)
Here's another option:
a = [1,2,2,3,3,4,5]
b = {}
a.each{|v|
b[v] ||= 0
b[v] += 1
}
b.select{|k, v| v == 1}.keys
and here's a potentially faster one (though more complex) that is hard coded to look for items "just listed once":
a = [1,2,2,3,3,4,5]
bad = {}
good = {}
a.each{|v|
if bad.key? v
# do nothing
else
if good.key? v
bad[v] = true
good.delete(v)
else
good[v] = true;
end
end
}
good.keys

Group array by nested array

I have an array in this format:
[
{ day: 1
intervals: [
{
from: 900,
to: 1200
}
]
},
{ day: 2
intervals: [
{
from: 900,
to: 1200
}
]
},
{ day: 3
intervals: [
{
from: 900,
to: 1200
}
]
},
{ day: 4
intervals: [
{
from: 900,
to: 1200
}
]
},
{ day: 5
intervals: [
{
from: 900,
to: 1200
}
]
},
{ day: 6
intervals: [
{
from: 900,
to: 1200
},
{
from: 1300,
to: 2200
}
]
},
{ day: 7
intervals: [
{
from: 900,
to: 1200
},
{
from: 1300,
to: 2200
}
]
}
]
I wan't to group them like this:
[
{ day: 1-5
intervals: [
{
from: 900,
to: 1200
}
]
},
{ day: 6-7
intervals: [
{
from: 900,
to: 1200
},
{
from: 1300,
to: 2200
}
]
}
]
Criteria:
Only group if intervals are the same.
Only group if matches are in chronological order, i.e 1-5 or 1-3, not 1-2-5.
How can this be achieved?
Here's a variation of #joelparkerhenderson's solution that tries to be a bit closer to your requirements re formatting of the output etc.
output = []
grouped = input.group_by do |x|
x[:intervals]
end
grouped.each_pair do |k, v|
days = v.map {|day| day[:day]}
if days.each_cons(2).all? { |d1, d2| d1.next == d2 }
output << {
:days => days.values_at(0,-1).join('-'),
:intervals => k
}
end
end
puts output
This produces the required output:
by_interval = data.inject({}) do | a, e |
i = e[:intervals]
a[i] ||= []
a[i] << e[:day].to_i
a
end
result = by_interval.map do | interval, days |
slices = days.sort.inject([]) do | a, e |
a << [] if a == [] || a.last.last != e - 1
a.last << e
a
end
slices.map do | slice |
{:day => "#{slice.first}-#{slice.last}", :intervals => interval }
end
end
result.flatten!
I'm sure there are better approaches :-)
You need to look into Map Method for an array. You need to remap the array and iterate over it to extract the data you want using your "grouping" logic above.
Extending #Michael Kohl's answer
output = []
grouped = schedules.as_json.group_by do |x|
x['intervals']
end
grouped.each_pair do |k, v|
days = v.map {|day| day['day']}
grouped_days = days.inject([[]]) do |grouped, num|
if grouped.last.count == 0 or grouped.last.last == num - 1
grouped.last << num
else
grouped << [ num ]
end
grouped
end
grouped_days.each do |d|
output << {
heading: d.values_at(0, -1).uniq.join('-'),
interval: k
}
end
end
output
You should probably split that up into separate methods, but you get the idea.

Resources