Creating an array in Ruby - arrays

I am trying to create an array in Ruby. When my function is called to print the array, it seems that the array is undefined. I am wondering if my syntax is off.
Can someone please tell me if this is the correct way to create and instantiate an array in one line, or if the problem is somewhere else in my code?
Below is my Ruby code:
class Game
words = Array["hat", "cat", "ate", "run", "eye", "soup", "date",
"bake", "wake", "grape", "apple", "pride", "drive",
"tacos", "linux", "orange", "purple", "volume",
"liquid", "palace", "molasses", "diamond", "sausage",
"america", "england"]
# starts the game state to play
def start_x
# game logic for begin
puts(words)
end
# if users wins
def win
puts("congratulations! you win!")
end
# if user loses
def death
puts("sorry! you die!")
end
end

You either need to constantize words into WORDS or you need to make those values available through a getter method or an instance variable. Here are some examples:
Constantize, making the array available to any caller:
class Game
WORDS = ['hat', 'cat', 'ate', 'run', 'eye', 'soup', 'date',
'bake', 'wake', 'grape', 'apple', 'pride', 'drive',
'tacos', 'linux', 'orange', 'purple', 'volume',
'liquid', 'palace', 'molasses', 'diamond', 'sausage',
'america', 'england']
def start_x
puts(WORDS)
end
end
And then it works:
⇒ Game.new.start_x
hat
cat
ate
run
eye
soup
date
bake
wake
grape
apple
pride
drive
tacos
linux
orange
purple
volume
liquid
palace
molasses
diamond
sausage
america
england
Or with a getter method:
class Game
def words
#words ||= ['hat', 'cat', 'ate', 'run', 'eye', 'soup', 'date',
'bake', 'wake', 'grape', 'apple', 'pride', 'drive',
'tacos', 'linux', 'orange', 'purple', 'volume',
'liquid', 'palace', 'molasses', 'diamond', 'sausage',
'america', 'england']
end
def start_x
puts(words)
end
end
Or with an instance variable:
class Game
def initialize
#words = ['hat', 'cat', 'ate', 'run', 'eye', 'soup', 'date',
'bake', 'wake', 'grape', 'apple', 'pride', 'drive',
'tacos', 'linux', 'orange', 'purple', 'volume',
'liquid', 'palace', 'molasses', 'diamond', 'sausage',
'america', 'england']
end
def start_x
puts(#words)
end
end
Or combine with an attribute reader:
class Game
attr_reader :words
def initialize
#words = ['hat', 'cat', 'ate', 'run', 'eye', 'soup', 'date',
'bake', 'wake', 'grape', 'apple', 'pride', 'drive',
'tacos', 'linux', 'orange', 'purple', 'volume',
'liquid', 'palace', 'molasses', 'diamond', 'sausage',
'america', 'england']
end
def start_x
puts(words)
end
end
All work the same way and will be used in different circumstances.

Your code doesn't work, because words is a local variable declared outside the methods.
You probably want to have an instance variable here. And it's usually a good idea to separate the game code from the data. So instead of hard-coding the words into the Game class, you pass the data upon initialization:
class Game
def initialize(words)
#words = words
end
def start_x
puts #words
end
# ...
end
To call it:
words = %w[
hat cat ate run eye soup date bake wake grape apple pride drive tacos linux
orange purple volume liquid palace molasses diamond sausage america england
]
game = Game.new(words)
game.start_x
From here on, you could easily extract the data into a words.txt file:
hat
cat
ate
...
sausage
america
england
And load the data via:
words = File.readlines('words.txt' chomp: true)
game = Game.new(words)
game.start_x
This allows you to launch your game with different sets of words without having to modify your code.

I guess this would help you.
class Game
attr_reader :words
def initialize
#words = %w[hat cat ate run eye soup date
bake wake grape apple pride drive
tacos linux orange purple volume
liquid palace molasses diamond sausage
america england]
end
# if users wins
def win
puts("congratulations! you win!")
end
# if user loses
def death
puts("sorry! you die!")
end
end
You can access the words like Game.new.words

Related

Plotting many pie charts using a loop to create a single figure using matplotlib

I'm having trouble converting a script I wrote to create and save 15 pie charts separately which I would like to save as a single figure with 15 subplots instead. I have tried taking fig, ax = plt.subplots(5, 3, figsize=(7, 7)) out of the loop and specifying the number of rows and columns for the plot but I get this error AttributeError: 'numpy.ndarray' object has no attribute 'pie'. This error doesn't occur if I leave that bit of code in the script as is seen below. Any help with tweaking the code below to create a single figure with 15 subplots (one for each site) would be enormously appreciated.
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_excel(path)
df_1 = df.groupby(['Site', 'group'])['Abundance'].sum().reset_index(name='site_count')
site = ['Ireland', 'England', 'France', 'Scotland', 'Italy', 'Spain',
'Croatia', 'Sweden', 'Denmark', 'Germany', 'Belgium', 'Austria', 'Poland', 'Stearman', 'Hungary']
for i in site:
df_1b = df_1.loc[df_1['Site'] == i]
colors = {'Dog': 'orange', 'Cat': 'cyan', 'Pig': 'darkred', 'Horse': 'lightcoral', 'Bird':
'grey', 'Rat': 'lightsteelblue', 'Whale': 'teal', 'Fish': 'plum', 'Shark': 'darkgreen'}
wp = {'linewidth': 1, 'edgecolor': "black"}
fig, ax = plt.subplots(figsize=(7, 7))
texts, autotexts = ax.pie(df_1b['site_count'],
labels=None,
shadow=False,
colors=[colors[key] for key in labels],
startangle=90,
wedgeprops=wp,
textprops=dict(color="black"))
plt.setp(autotexts, size=16)
ax.set_title(site, size=16, weight="bold", y=0)
plt.savefig('%s_group_diversity.png' % i, bbox_inches='tight', pad_inches=0.05, dpi=600)
It's hard to guess how exactly you'd like the plot to look like.
The main changes the code below makes, are:
adding fig, axs = plt.subplots(nrows=5, ncols=3, figsize=(12, 18)). Here axs is a 2d array of subplots. figsize should be large enough to fit the 15 subplots.
df_1b['group'] is used for the labels that decide the color (it's not clear where the labels themselves should be shown, maybe in a common legend)
autopct='%.1f%%' is added to ax.pie(...). This shows the percentages with one decimal.
With autopct, ax.pie(...) now returns 3 lists: texts, autotexts, wedges. The texts are the text objects for the labels (currently empty texts), autotexts are the percentages (that are calculated "automatically"), wedges are the triangular wedges.
ax.set_title now uses the site name, and puts it at a negative y-value (y=0 would overlap with the pie)
plt.tight_layout() at the end tries to optimize the surrounding white space
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
site = ['Ireland', 'England', 'France', 'Scotland', 'Italy', 'Spain',
'Croatia', 'Sweden', 'Denmark', 'Germany', 'Belgium', 'Austria', 'Poland', 'Stearman', 'Hungary']
colors = {'Dog': 'orange', 'Cat': 'cyan', 'Pig': 'darkred', 'Horse': 'lightcoral', 'Bird': 'grey',
'Rat': 'lightsteelblue', 'Whale': 'teal', 'Fish': 'plum', 'Shark': 'darkgreen'}
wedge_properties = {'linewidth': 1, 'edgecolor': "black"}
# create some dummy test data
df = pd.DataFrame({'Site': np.random.choice(site, 1000),
'group': np.random.choice(list(colors.keys()), 1000),
'Abundance': np.random.randint(1, 11, 1000)})
df_1 = df.groupby(['Site', 'group'])['Abundance'].sum().reset_index(name='site_count')
fig, axs = plt.subplots(nrows=5, ncols=3, figsize=(12, 18))
for site_i, ax in zip(site, axs.flat):
df_1b = df_1[df_1['Site'] == site_i]
labels = df_1b['group']
texts, autotexts, wedges = ax.pie(df_1b['site_count'],
labels=None,
shadow=False,
colors=[colors[key] for key in labels],
startangle=90,
wedgeprops=wedge_properties,
textprops=dict(color="black"),
autopct='%.1f%%')
plt.setp(autotexts, size=10)
ax.set_title(site_i, size=16, weight="bold", y=-0.05)
plt.tight_layout()
plt.savefig('group_diversity.png', bbox_inches='tight', pad_inches=0.05, dpi=600)
plt.show()

In a Ruby on Rails app, I'm trying to loop through an array within a hash within an array. Why am I getting "syntax error" message?

I have a Ruby on Rails application to enter results and create a league table for a football competition.
I'm trying to input some results by creating records in the database through heroku and I get error messages.
The application isn't perfectly designed: to enter the results, I have to create the fixtures and enter the score for each team. Then, independently I have to record each goal scorer, creating a record for each goal which is either associated with an existing player or requires me to firstly create a new player and then create the goal.
When I ran the code below heroku, I got this error:
syntax error, unexpected ':', expecting keyword_end
Maybe I'm missing something simple about lopping through an array within a hash?
Thank you for any advice!
coalition = Team.find_by(name: "Coalition")
moscow_rebels = Team.find_by(name: "Moscow Rebels")
red_star = Team.find_by(name: "Red Star")
unsanctionables = Team.find_by(name: "The Unsanctionables")
cavalry = Team.find_by(name: "Cavalry")
galactics = Team.find_by(name: "The Galactics")
happy_sundays = Team.find_by(name: "Happy Sundays")
hardmen = Team.find_by(name: "Hardmen")
international = Team.find_by(name: "International")
evropa = Venue.find_by(name: "Evropa")
s28 = Season.find_by(number: 28)
start_time = DateTime.new(2020,9,6,11,0,0,'+03:00')
scheduled_matches_1 =
[
{team_1: cavalry, team_1_goals: 1, team_1_scorers: ["Minaev"], team_2_goals: 6, team_2_scorers: ["Kovalev", "Kovalev", "Kovalev", "Thomas", "Thomas", "Grivachev"], team_2: coalition, time: start_time, venue: evropa, season: s28},
{team_1: hardmen, team_1_goals: 4, team_1_scorers: ["Jones", "Jones", "Jones", "Fusi"], team_2_goals: 2, team_2_scorers: ["Kazamula", "Ario"], team_2: galactics, time: start_time + 1.hour, venue: evropa, season: s28},
{team_1: international, team_1_goals: 9, team_1_scorers: ["Kimonnen", "Kimonnen", "Kimonnen", "Burya", "Burya", "Zakharyaev", "Zakharyaev", "Lavruk", "Rihter"], team_2_goals: 0, team_2_scorers: [], team_2: happy_sundays, time: start_time+2.hours, venue: evropa, season: s28}
]
scheduled_matches.each do |match|
new_fixture = Fixture.create(time: match[:time], venue: match[:venue], season: match[:season])
tf1 = TeamFixture.create(team: match[:team_1], fixture: new_fixture)
tf2 = TeamFixture.create(team: match[:team_2], fixture: new_fixture)
ts1 = TeamScore.create(team_fixture: tf1, total_goals: match{:team_1_goals})
ts2 = TeamScore.create(team_fixture: tf2, total_goals: match{:team_2_goals})
match[:team_1_scorers].each do |scorer|
if Player.exists?(team: tf1.team, last_name: scorer)
Goal.create(team_score: ts1, player: Player.find_by(last_name: scorer))
else
new_player = Player.create(team: tf1.team, last_name: scorer)
Goal.create(team_score: ts1, player: new_player)
end
end
match[:team_2_scorers].each do |scorer_2|
if Player.exists?(team: tf2.team, last_name: scorer_2)
Goal.create(team_score: ts2, player: Player.find_by(last_name: scorer_2))
else
new_player = Player.create(team: tf2.team, last_name: scorer_2)
Goal.create(team_score: ts2, player: new_player)
end
end
end
It looks like you are using braces when you meant to use brackets to access the hash. Below is one of the issues, but the same issue is in ts2.
ts1 = TeamScore.create(team_fixture: tf1, total_goals: match{:team_1_goals})
should be match[:team_1_goals]
ts1 = TeamScore.create(team_fixture: tf1, total_goals: match[:team_1_goals])
It may be because you have scheduled_matches_1 at the top and scheduled_matches.each do... further down.
But the real issue here is that your variable names match the data content, rather than being used to hold the content. If a new team joins your league, you have to change the code. Next week, you are going to have to change the hard-coded date value. Your scheduled_matches_1 data structure includes the active record objects returned by the first set of Team.findByName() calls. It would be easier to fetch these objects from the database inside your loops, and just hold the team name as a string in the hash.
There is some duplication too. Consider that each fixture has a home team and an away team. Each team has a name, and an array (possibly empty) of the players who scored. We don't need the number of goals; we can just count the number of players in the 'scorers' array. The other attributes, like the location and season belong to the fixture, not the team. So your hash might be better as
{
"fixtures": [
{
"home": {
"name": "Cavalry",
"scorers": [
"Minaev"
]
},
"away": {
"name": "Coalition",
"scorers": [
"Kovalev",
"Kovalev",
"Kovalev",
"Thomas",
"Thomas",
"Grivachev"
]
},
"venue": "Evropa",
"season": "s28"
}
]
}
because then you can create a reusable method to process each team. And maybe create a new method that returns the player (which it either finds or creates) which can be called by the loop that adds the goals.
Also, as it stands, I'm not sure the code can handle 'own goals', either. Perhaps something for a future iteration :)

can we delete element in list based on certain condition

I have a Python list:
test_list = ['LeBron James', 'and', 'Nina Mata', 'Dan Brown', 'Derrick Barnes', 'and',
'Gordon C. James', 'J. Kenji López-Alt', 'and', 'Gianna Ruggiero', ]
I want output like this:
final_list = ['LeBron James and Nina Mata', 'Dan Brown', 'Derrick Barnes and
Gordon C. James', 'J. Kenji López-Alt and Gianna Ruggiero']
In short, I want one item before 'and' one item after 'and' to be combined. On the other hand, names coming without 'and' should not be combined and left as it is. How can we do this in Python?
Here's a solution perhaps not too elegant but simple and functional: join all words with a glue symbol that does not happen in any of them (e.g., "~"), replace the resulting "~and~"s with " and "s, and split by the glue symbol again:
"~".join(test_list).replace("~and~", " and ").split("~")
#['LeBron James and Nina Mata', 'Dan Brown',
# 'Derrick Barnes and Gordon C. James',
# 'J. Kenji López-Alt and Gianna Ruggiero']
This should work for groups with more than one "and," too.

loop through implicit array

I've been stuck on this for awhile now. I'm trying to loop through this array so I can perform some calculations but I cannot figure out how to loop through there values. Any suggestions?
I managed to figure out how to get there collection structures but I want to loop through each structure and grab there values as well and thats what I'm stuck on.
Also, I want to refrain from using cfscript if possible as I'm still in the learning stages of learning coldfusion.
Here is my code:
<cfset houseStuff = {
Bedroom = [
'Luxury Duvet Set with Alternative Down Comforter',
'Accent Coverlet & Shams',
'Two Sets of Luxurious Liens',
'Mattress Pad',
'Blanket',
'Six Bed Pillows',
'Clock Radio',
'Twenty Hangers'
],
Bathroom = [
'Four Bath Towels',
'Four Hand Towels',
'Four Face Towels',
'Bath Rug',
'Shower Curtain',
'Stainless Tooth Brush Holder & Soap Dish',
'Wastebasket',
'Artwork',
'Hair Dryer',
'Toilet Brush & Plunger'
],
Dining = [
'Dinnerware',
'Place Mats',
'Napkins',
'Flatware',
'Glassware & Wine Glasses'
],
Kitchen = [
'Microwave',
'Cookware',
'Mixing Bowls',
'Baking Dish',
'Colander',
'Stainless Utensil Holder',
'Large Fork',
'Large Spoon',
'Spatula',
'Whisk',
'Measuring Spoon & Cup',
'Carving & Paring Knives',
'Four Steak Knives',
'Cutting Board',
'Salt & Pepper Set',
'Wine Opener',
'Coffee Maker',
'Toaster',
'Electric Can Opener',
'Flatware Tray',
'Kitchen Wastebasket',
'Dish Towels',
'Pot Holders',
'Pitcher',
'10" Non-Stick Frying Pan',
'Cookie Sheet',
'Stainless Steel Electric Tea Kettle',
'3 Piece Non-Metal (Spatula, Spoon, Paste Spoon) Combo'
],
Micellaneous = [
'Iron & Cutting Board',
'Cordless Dual Phone with Digital Answering Machine',
'Broom',
'Dust Pan',
'Vacuum',
'Decor',
'Laundry Basket'
],
StarterKit = [
'Bath Tissue',
'Soap',
'Shampoo & Conditioner',
'Paper Towels',
'Sponge',
'Laundry Soap',
'Dishwasher Detergent',
'Liquid Dish Soap',
'Extra Light Bulbs',
'Coffee',
'Sugar',
'Creamer',
'Bottled Water',
'Oatmeal',
'Breakfast Bars',
'Peanuts',
'Chips',
'Mints',
'Welcome Information'
],
MasterBedroom = [
'Queen bed',
'Headboard',
'Two Nightstands',
'Dresser & Mirrior',
'Two Lamps',
'Artwork',
'LCD Television'
],
LivingRoom = [
'Sofa',
'Chair',
'End Table',
'Coffee Table',
'Lamp',
'LCD TV w/stand',
'DVD Player',
'Artwork'
],
DiningRoom = [
'Dining Table',
'Dining Chairs',
'Artwork'
],
OfficePackage = [
'Desk',
'Chair',
'Lamp'
],
AdditionalBedrooms = [
'Queen or Two Twin Beds',
'Headboard',
'Nightstand',
'Chest of Drawers',
'Lamp',
'Artwork'
]
} />
<cfloop collection="#houseStuff#" item="key">
<cfdump var="#key#"> <br>
<!--- <p style="color:##fff;">#key#:</p> <br /> --->
</cfloop>
Nevermind, I finally figured it out. I had to loop through the collection first. Once I do that create another loop inside it to loop over it's structured values.
<cfloop collection="houseStuff" item="key">
<!---<cfdump var="#houseStuff[key]"> --->
<cfloop from="1" to="#arrayLen(houseStuff[key])#" index="j">
#j#
</cfloop>
</cfloop>
I know you said you'd prefer tags instead of script, but if you are in the learning stages of ColdFusion, I'd still recommend learning how to properly use cfscript. In addition to making your CF a little bit cleaner, it will also make your life a lot easier, especially for things like looping.
Outputting all elements becomes:
<cfscript>
for ( i in houseStuff ) { // loop over the outer Structure
writeOutput(i & ":<br>") ;
for ( j in houseStuff[i] ) { // loop over each inner Array key
writeOutput(j & "<br>") ;
}
writeOutput("<br>");
}
</cfscript>
https://trycf.com/gist/898988f6969a57aa5dece39c42037cfd/acf?theme=monokai
... which, in this context, does get into the philosophical discussion of whether to write output code in tags or script and goes slightly beyond the scope of this question. But I've always been a proponent of learning best-practices at the same time as the basics. Personally, I do tend to follow the tags-for-output view, but for basic looping, the script version is a bit cleaner to me. I'd learn both.
Also check out: http://www.learncfinaweek.com. There's a section in there on Looping with both methods.

Ruby match string in array

I have a string like that: "Men's Beech River Cable T-Shirt" how can I get category from this string?
str = "Men's Beech River Cable T-Shirt"
str2 = "MEN'S GOOSE EYE MOUNTAIN DOWN VEST"
cat1 = str1.split.last # T-Shirt
cat2 = str2.split.last # VEST
TOPS = %w(jacket vest coat blazer parka sweater shirt polo t-shirt)
Desired result:
category_str1 = "Tops" # Since T-Shirt (shirt) is in TOPS constant.
category_str2 = "Tops" # Since vest is in TOPS const.
I don't know how to describe my problem better, I hope you understand it from example provided.
str = "Men's Beech River Cable T-Shirt"
cat_orig = str.split.last # T-Shirt
TOPS = %w(jacket vest coat blazer parka sweater shirt polo)
RE_TOPS = Regexp.union(TOPS)
category = "Tops" if RE_TOPS =~ cat_orig.downcase
Note there are no comma's in the %w() style array syntax.
str = "Men's Beech River Cable T-Shirt"
cat_orig = str.split.last # T-Shirt
TOPS = %w(jacket vest coat blazer parka sweater shirt polo) # suppressed the comma to get a clean array
category = "Tops" if !cat_orig[/(#{TOPS.join("|")})/i].nil?
The join on the TOPS Array build an alternative regex of the form:
(jacket|vest|coat|blazer|parka|sweater|shirt|polo)
If any of those word is present in cat_orig, the return will be the matched word, if not it will return nil.
Note the leading i in the regex to makes it case insensitive.
The best way to do this is through a hash, not an array. Let's say your caetgories look something like this
categories = { "TOPS" => ["shirt", "coat", "blazer"],
"COOKING" => ["knife", "fork", "pan"] }
We can then loop through each category and find if their values include the word in the string
categories.each do |key, value|
puts key if str.downcase.split(' ').any? { |word| categories[key].include?(word) }
end
Loop through each category, and find if the category has a word that the string has.
Note: This does not yet search for substrings.

Resources