Sample code:
defmodule Foo do
def fast_cars_cache do
fast_cars =
{
%{color: "Red", make: "Mclaren", mileage: 15641.469},
%{color: "Blue", make: "Ferrari", mileage: 120012.481},
%{color: "Red", make: "Ferrari", mileage: 29831.021},
%{color: "Black", make: "Ferrari", mileage: 24030.674},
%{color: "Cobalt", make: "Ferrari", mileage: 412.811},
%{color: "Blue", make: "Koenigsegg", mileage: 250.762},
%{color: "Cobalt", make: "Koenigsegg", mileage: 1297.76},
%{color: "Titanium", make: "Koenigsegg", mileage: 5360.336},
%{color: "Blue", make: "Maserati", mileage: 255.78}
}
if Enum.member?(:ets.all(), :fast_cars_cache) do
:ets.delete(:fast_cars_cache)
:ets.new(:fast_cars_cache, [:duplicate_bag, :public, :named_table])
:ets.insert(:fast_cars_cache, fast_cars)
else
:ets.new(:fast_cars_cache, [:duplicate_bag, :public, :named_table])
:ets.insert(:fast_cars_cache, fast_cars)
end
end
def update_record do
fast_cars_cache()
old_record = :ets.first(:fast_cars_cache)
new_record =
%{color: "Black", make: old_record.make, mileage: 1641.469}
:ets.delete(:fast_cars_cache, {old_record})
|> IO.inspect(label: "Old record deleted")
:ets.insert(:fast_cars_cache, {new_record})
:ets.tab2list(:fast_cars_cache)
end
end
Output:
iex(1)> Foo.update_record
Old record deleted: true
[
{%{color: "Red", make: "Mclaren", mileage: 15641.469},
%{color: "Blue", make: "Ferrari", mileage: 120012.481},
%{color: "Red", make: "Ferrari", mileage: 29831.021},
%{color: "Black", make: "Ferrari", mileage: 24030.674},
%{color: "Cobalt", make: "Ferrari", mileage: 412.811},
%{color: "Blue", make: "Koenigsegg", mileage: 250.762},
%{color: "Cobalt", make: "Koenigsegg", mileage: 1297.76},
%{color: "Titanium", make: "Koenigsegg", mileage: 5360.336},
%{color: "Blue", make: "Maserati", mileage: 255.78}},
{%{color: "Black", make: "Mclaren", mileage: 1641.469}}
]
Observations/Questions:
According to the IO.inspect, old_record was deleted yet, as tab2list reveals, this record still exists. Why is that?
If, in fact, old_record was never deleted, what adjustments does the code need to accomplish this?
Ideally, I'd like to make use of :ets.select_replace, if applicable, to perform this update all in one step but I can't make heads or tails of the stipulations for the match specification requirement. It'd be really helpful if someone could disambiguate it with an example or two based on the sample above.
As always, thanks so much for your helpful guidance and suggestions :)
You should clearly distinguish maps and tuples. Map is a key-value structure. Tuple is not.
:ets records are tuples, not maps.
Your initial structure is a tuple of maps. One single tuple, having many maps. It gets inserted as a single element into :ets, which is clearly visible in your output (check curly braces.)
I would guess you were to insert many elements into the cache.
iex|1 ▶ :ets.new(:fast_cars_cache, [:duplicate_bag, :public, :named_table])
iex|2 ▶ fast_cars = [
...|2 ▶ {"Red", "Mclaren", 15641.469},
...|2 ▶ {"Blue", "Ferrari", 120012.481},
...|2 ▶ {"Red", "Ferrari", 29831.021}
...|2 ▶ ]
iex|3 ▶ :ets.insert(:fast_cars_cache, fast_cars)
:ets.first/1, as it’s stated in the documentation, returns the first key. :ets.detele/2 deletes a record by key and your code wraps whatever was returned from :ets.first/1 into a one-element tuple, making :ets.delete/2 a no-op no matter what (and yes, :ets.delete/2 always returns true.)
iex|4 ▶ :ets.first(:fast_cars_cache)
#⇒ "Red"
iex|5 ▶ :ets.delete(:fast_cars_cache, {"Red"}) # NOOP
#⇒ true
iex|6 ▶ :ets.delete(:fast_cars_cache, "Red") # DELETED
#⇒ true
Then if you want to insert a new record, create a tuple and insert it, don’t create a map wrapped into a single-element tuple. To get the old record by key, one usually uses :ets.lookup/2, or more sophisticated :ets.select.
iex|7 ▶ [{_, make, _}|_] = :ets.lookup(:fast_cars_cache, "Red")
iex|8 ▶ new_record = {"Black", make, 1641.469}
iex|9 ▶ :ets.insert(:fast_cars_cache, new_record)
I have linked enough documentation to leave using :ets.select_replace/2 as homework.
Related
I am new to ruby on rails and trying to make a multidimensional hash from different arrays.
persons= [person1, person2, person3, person4]
projects= [project1, project2, project3]
issues= [1000, 10001, 1002, 1003, 1004,1005,1006,1007,1008,1009,1010]
issuetime = [1, 2 , 3, 4, 5]
I want a Hash like this:
hash = {person 1 =>{project1 => {1000, 1001, 1002 => {1,2,3}}}, person2 =>{project1 => {1003, 1004, 1005 => {3,4,5}}}}
I tried:
hash= {}
persons.each_with_index do [person,i]
if hash.has_key?(person)
hash[person] << projects[i]
else
hash[person] = [projects[i]]
end
end
This is working but it shows me only:
hash = {{person 1 =>{project1}}, {person2=>{project2}}}.
I want a multidimensional Hash if this is possible. I dont know how to access the next key + value to build a multidimensional hash.
Thanks for your help!
persons = ['person1', 'person2', 'person3']
projects = ['project1', 'project2', 'project3']
issues = [1000, 10001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010]
issuetimes = [1, 2 ,3, 4, 5, 6, 7, 8, 9]
persons.each_with_object({}).with_index do |(person, hsh), index|
hsh[person] = {
projects[index] => {
issues.slice!(0, 3) => issuetimes.slice!(0, 3)
}
}
end
This will give you. {"person1"=>{"project1"=>{[1000, 10001, 1002]=>[1, 2, 3]}}, "person2"=>{"project2"=>{[1003, 1004, 1005]=>[4, 5, 6]}}, "person3"=>{"project3"=>{[1006, 1007, 1008]=>[7, 8, 9]}}}
A couple of things to note about this question.
{1000, 1001, 1002 => {1,2,3}} is not a valid hash, as it will throw an error. However, you can do the following [1, 2, 3]=>[4, 5, 6]}, so my answer above assumes that is what you want to do.
My solution uses the destructive form of slice!, so note that the issues and issuetimes arrays will be changed from this solution. If you don't want that I would make duplicates of those arrays using the dup method.
On a somewhat related note, you might want to check out zip when it comes to creating hashes from arrays. Hash[persons.zip(projects)] // {"person1"=>"project1", "person2"=>"project2", "person3"=>"project3"}, which is not what you want here, but I thought I would mention it.
I have a multi-dimensional array that contains a lot of information about various objects. I'm looking to remove all but the first instance of arrays that contain similar information:
multi_array = [
["Nissan", "Altima", "tan", "2016", "80000"],
["Ford", "F-150", "silver", "2012", "120000"],
["Nissan", "Altima", "red", "2009", "50000"],
["Audi", "A4", "blue", "2014", "30000"]
]
In the above example I want to remove any of the subarrays that have the instance of "Altima" in it so that result would be:
fixed_multi_array = [
["Nissan", "Altima", "tan", "2016", "80000"],
["Ford", "F-150", "silver", "2012", "120000"],
["Audi", "A4", "blue", "2014", "30000"]
]
What's the fastest way to do this in ruby (or Ruby on Rails)?
Update:
Should have clarified, I'm looking to de-duplicate based on a value that's always in the same position of the sub-arrays. So, in example above I'm always looking to de-dupe only on value in position 1 of the sub-arrays.
You can use uniq:
fixed_multi_array = multi_array.uniq{|x| x[1]}
Demonstration
Here is one more way to do this:
multi_array.group_by {|i| i[1]}.values.map(&:first)
I have an array as shown below:
[
[
"[\"\", \"Mrs. Brain Bauch\", \"Vernice Ledner\"]",
"[\"\", \"Robb Ratke\", \"Amaya Jakubowski\"]",
"[\"\", \"Lindsey Cremin III\", \"Harvey Fisher\"]",
"[\"\", \"Daniela Schneider\", \"Benny Schumm\"]"
]
]
How can I convert this into the array structure shown below:
[
[
["Mrs. Brain Bauch", "Vernice Ledner"],
["Robb Ratke", "Amaya Jakubowski"],
["Lindsey Cremin III", "Harvey Fisher"],
["Daniela Schneider", "Benny Schumm"]
]
]
require 'json'
input = [[
"[\"\", \"Mrs. Brain Bauch\", \"Vernice Ledner\"]",
"[\"\", \"Robb Ratke\", \"Amaya Jakubowski\"]",
"[\"\", \"Lindsey Cremin III\", \"Harvey Fisher\"]",
"[\"\", \"Daniela Schneider\", \"Benny Schumm\"]"
]]
[input.first.map { |l| JSON.parse l }.map { |a| a.reject &:empty? }]
#⇒ [[
# ["Mrs. Brain Bauch", "Vernice Ledner"],
# ["Robb Ratke", "Amaya Jakubowski"],
# ["Lindsey Cremin III", "Harvey Fisher"],
# ["Daniela Schneider", "Benny Schumm"]
# ]]
If arr is your array:
r = /
(?<=\") # match `\"` in a positive lookbehind
[A-Z] # match a capital letter
[a-z\.\s]+ # match a letter, period or space one or more times
/ix # case-insenitive (i) and free-spacing (x) regex definition modes
[arr.first.map { |s| s.scan r }]
#=> [[["Mrs. Brain Bauch", "Vernice Ledner"],
# ["Robb Ratke", "Amaya Jakubowski"],
# ["Lindsey Cremin III", "Harvey Fisher"],
# ["Daniela Schneider", "Benny Schumm"]]]
I am doing my Ruby on Rails project now. PostgreSQL is used as database.I have a table called pets, where stores all pets information(name, gender, age, etc.), which are shown as follows.
{Pet id: 1, name: "Jean", gender: "Female", pet_type: "Cat", age: "0", body_weight: 1, breed: "Abyssinian", created_at: "2015-04-26 23:50:01", updated_at: "2015-04-26 23:50:01"}
{Pet id: 2, ...}
...
{Pet id: 10, ...}
Now I am going to check whether there is a Abyssinian breed cat in table pets. If there is, return the Pet id, otherwise return "no such cat found".
Is there anyone know how to do this? Thanks in advance.
To find the first pet where breed == "Abyssinian":
pet = Pet.find_by(breed: "Abyssinian", pet_type: "Cat")
So, to combine that with your required logic:
pet = Pet.find_by(breed: "Abyssinian", pet_type: "Cat")
output = pet ? pet : "no such cat found"
You could simply this even further to:
output = Pet.find_by(breed: "Abyssinian", pet_type: "Cat") || "no such cat found"
Is "breed" a field in the pets table? If so, you could do something like:
if Pet.exists?( breed: "Abyssinian" )
x = Pet.where( breed: "Abyssinian" )
else
x = "No such cat found"
end
The danger in this is that there could be multiple Pets returned. If you want to find one and only one, you could do
if Pet.where( breed: "Abyssinian" ).count == 1
instead.
REVISION
#infused's answer is more succinct, I would encourage an implementation similar to his, assuming a breed column exists.
I've created this simple example script to output a list of foods. If the food is a fruit then the color of the fruit will be displayed as well. The issue I'm having is in dealing with the irregular pluralization of 'strawberries.'
fruits = {apple = "green", orange = "orange", stawberry = "red"}
foods = {"potatoes", "apples", "strawberries", "carrots", "crab-apples"}
for _, food in ipairs(foods) do
for fruit, fruit_colour in pairs(fruits) do
duplicate = false
if (string.match(food, "^"..fruit) or string.match((string.gsub(food, "ies", "y")), "^"..fruit)) and not(duplicate) then -- this is where the troubles is!
print(food.." = "..fruit_colour)
duplicate = true
break
end
end
if not(duplicate) then
print(food)
end
end
Right now the program outputs:
potatoes
apples = green
strawberries
carrots
crab-apples
What I want is:
potatoes
apples = green
strawberries = red
carrots
crab-apples
I can't figure out why this doesn't work like I want it to!
Well for one thing, you miss-spelled strawberry here:
fruits = {apple = "green", orange = "orange", stawberry = "red"}
You can also work with lua tables as sets, which means that nested loop searching for duplicates is unnecessary. It can be simplified to something like:
fruits = {apple = "green", orange = "orange", strawberry = "red"}
foods = {"potatoes", "apples", "strawberries", "carrots", "crab-apples"}
function singular(word)
return word:gsub("(%a+)ies$", "%1y"):gsub("(%a+)s$", "%1")
end
for _, food in ipairs(foods) do
local single_fruit = singular(food)
if fruits[single_fruit] then
print(food .. " = " .. fruits[single_fruit])
else
print(food)
end
end
stawberry should be strawberry. The loop changes strawberries to strawberry and then tries to match strawberry against ^stawberry, but the typo causes it not to match.