Create array from given array - arrays

Is it possible to create an Array from another Array?
Lang: Ruby on Rails
Case
Workers are entitled to fill in their own work hours. Sometimes they forget to do it. This is what I want to tackle. In the end, I want an Array with time codes of periods the worker forgot to register his hours.
timecodes = [201201, 201202, 201203, 201204, 201205, 201206, 201207, 201208, 201209, 201210, 201211, 201212, 201213, 201301, 201302, 201304, 201305, 201306, ...]
Worker works from 201203 to 201209 with us.
timecards = [201203, 201204, 201205, 201207, 201208, 201209]
As you see, he forgot to register 201206.
What I want to do
# Create Array from timecode on start to timecode on end
worked_with_us = [201203, 201204, 201205, 201206, 201207, 201208, 201209]
#=> This is the actual problem, how can I automate this?
forgot_to_register = worked_with_us.?????(timecards)
forgot_to_register = worked_with_us - timecards # Thanks Zwippie
#=> [201206]
Now I know which period the worker forgot to register his hours.
All together
How can I create an Array from another Array, giving a start and end value?

You can just subtract arrays with - (minus):
[1, 2, 3] - [1, 3] = [2]
To build an array with years/months, this can be done with a Range, but this only works if you build an array for each year, something like:
months = (2012..2013).map do |year|
("#{year}01".."#{year}12").to_a.collect(&:to_i)
end.flatten
=> [201201, 201202, 201203, 201204, 201205, 201206, 201207, 201208, 201209, 201210, 201211, 201212, 201301, 201302, 201303, 201304, 201305, 201306, 201307, 201308, 201309, 201310, 201311, 201312]
And for the function to create those ranges dynamically:
def month_array(year_from, year_to, month_from=1, month_to=12)
(year_from..year_to).map do |year|
# Correct from/to months
mf = year_from == year ? month_from : 1
mt = year_to == year ? month_to : 12
(mf..mt).map do |month|
("%d%02d" % [year, month]).to_i
end
end.flatten
end
Update: You wanted other input parameters for this method, but I hope you can work that out yourself. :)

Related

Ruby: the closest date to specific date

I have a problem with my ruby script. I have an array
files = ["2020-09-14.access","2020-09-13.access","2020-09-11.access","2020-09-10.access","2020-09-09.access","2020-09-08.access","2020-09-07.access","2020-09-05.access","2020-09-04.access","2020-09-02.access","2020-09-01.access","2020-09-14.sale","2020-09-12.sale","2020-09-08.sale","2020-09-07.sale","2020-09-06.sale","2020-09-04.sale",]
that contains values that are file names. There are two types of files: access and sale. Every file name contains date of file creating. From each file type I want to get only these values with older dates beginning form file created two days ago. For the file type sale there is no problem, today is 2020-09-14, file created two days ago is 2020-09-12.sale. But in case access files there is no file created 2020-09-12 so I want file with the closest date to 2020-09-12 which means value 2020-09-10.access and I'm stack in here. In short I want to get array like this
to_del_files = [["2020-09-10.access","2020-09-09.access","2020-09-08.access","2020-09-07.access","2020-09-05.access","2020-09-04.access","2020-09-02.access","2020-09-01.access"],["2020-09-12.sale","2020-09-08.sale","2020-09-07.sale","2020-09-06.sale","2020-09-04.sale"]]
My code is below:
require 'date'
files = ["2020-09-14.access","2020-09-13.access","2020-09-10.access","2020-09-09.access","2020-09-08.access","2020-09-07.access","2020-09-05.access","2020-09-04.access","2020-09-02.access","2020-09-01.access","2020-09-14.sale","2020-09-12.sale","2020-09-08.sale","2020-09-07.sale","2020-09-06.sale","2020-09-04.sale",]
names = files.map {|x| x.split('.')[1] }.uniq
puts names
date = Date.today
date2ago = date -2
to_del_files = []
names.each do |item|
tmp = files.select { |x| x =~ /#{item}/ }
flag = tmp.select {|x| x =~ /#{date2ago}/ }
if flag.size > 0
index = tmp.find_index("#{flag[0]}")
to_del_files << tmp[index..-1]
else
#what to do in case where there is no such date in files
end
end
puts to_del_files
Thanks for any help.
In order for you to get the files to delete:
def old_files(files, date)
files.sort.filter { |file| Date.parse(file) < date }
end
And then you can use:
files = ["2020-09-14.access","2020-09-13.access","2020-09-10.access","2020-09-09.access","2020-09-08.access","2020-09-07.access","2020-09-05.access","2020-09-04.access","2020-09-02.access","2020-09-01.access","2020-09-14.sale","2020-09-12.sale","2020-09-08.sale","2020-09-07.sale","2020-09-06.sale","2020-09-04.sale",]
today = Date.today
date = today -2
to_del_files = old_files(files, date)
I understand you wish to select elements from files corresponding to dates that are equal to or earlier than a given date. If that is correct you can do that as follows.
files = [
"2020-09-14.access", "2020-09-13.access", "2020-09-11.access",
"2020-09-10.access", "2020-09-09.access", "2020-09-08.access",
"2020-09-07.access", "2020-09-05.access", "2020-09-04.access",
"2020-09-02.access", "2020-09-01.access", "2020-09-14.sale",
"2020-09-12.sale", "2020-09-08.sale", "2020-09-07.sale",
"2020-09-06.sale", "2020-09-04.sale"
]
require 'date'
def files_on_or_before_date(arr)
files_on_or_before_target_date(arr, Date.now-2)
end
def files_on_or_before_target_date(arr, target_date)
arr.select { |d| Date.strptime(d, '%Y-%m-%d') <= target_date }
end
files_on_or_before_target_date(files, Date.new(2020, 9, 12))
#=> ["2020-09-11.access", "2020-09-10.access", "2020-09-09.access",
# "2020-09-08.access", "2020-09-07.access", "2020-09-05.access",
# "2020-09-04.access", "2020-09-02.access", "2020-09-01.access",
# "2020-09-12.sale", "2020-09-08.sale", "2020-09-07.sale",
# "2020-09-06.sale", "2020-09-04.sale"]
files_on_or_before_target_date(files, Date.new(2020, 9, 10))
#=> ["2020-09-10.access", "2020-09-09.access", "2020-09-08.access",
# "2020-09-07.access", "2020-09-05.access", "2020-09-04.access",
# "2020-09-02.access", "2020-09-01.access", "2020-09-08.sale",
# "2020-09-07.sale", "2020-09-06.sale", "2020-09-04.sale"]
These return values can of course be added to an array.
See Date::strptime and DateTime#strftime, the latter for date formatting directives.
Date.strptime("2020-09-14.access", '%Y-%m-%d')
returns the same Date object as does
Date.strptime("2020-09-14", '%Y-%m-%d')
To guard against possible future change in the implementation of Date::strptime strptime's argument d could be replaced with d[/[^.]+/] or d[0, d.index('.')], both of which become "2020-09-14" when d = "2020-09-14.access".

How could I do the sum of all values of a nested hash?

I have a nested hash like this
Aranea={
"Aranéomorphes"=>{
"Agelenidae"=>[80,1327],
"Amaurobiidae"=>[49,270],
"Ammoxenidae"=>[4,18],
"Anapidae"=>[58,233],
"Anyphaenidae"=>[56,572],
"Araneidae"=>[175,3074],
"Archaeidae"=>[5,90],
"Arkydiae"=>[2,38],
"Austrochilidae"=>[3,10],
"Caponiidae"=>[18,119],
"Cheiracanthiidae"=>[12,353],
"Cithaeronidae"=>[2,8],
"Clubionidae"=>[16,639],
"Corinnidae"=>[68,489],
"Ctenidae"=>[48,519],......
For each key (spiders families), the array represents [number of genders, number of species].
Iwould like to get the sum of all first elements....i.e all the genders in total....
I tried different things without success like :
genre = []
#total = genre.transpose.map {|x| x.reduce(:+)}
Or....
def sum_deeply(h)
h.values.inject(0) { |m, v|
m + (Hash === v[0] ? sum_deeply(v[0]) : v[0].to_i)
}
end
puts sum_deeply(Aranea)
But none does work for with transpose I get a no implicit conversion error...
Could anyone enligthen me on this ? Thanks
!!! Update.... 08.07.2020... solution found with
families = Aranea
num_genders = families.flat_map do |_family_name, species_hash|
num_genders, _num_species = species_hash.values.transpose
num_genders
Thanks to Kache for his help on this.
This should do what you want:
families = Aranea
num_genders = families.flat_map do |_family_name, species_hash|
num_genders, _num_species = species_hash.values.transpose
num_genders
end
num_genders.inject(:+)
Just a tip: splitting out the "data extraction" and "data processing" (i.e. accessing the num_genders value vs summing them) will make your code easier to follow.
I don't think there'll be any part of the above that you won't understand, but if there is, just let me know what parts you'd like to have explained.

Ruby: Extract elements from deeply nested JSON structure based on criteria

Want to extract every marketID from every market that has a marketName == 'Moneyline'. Tried a few combinations of .maps, .rejects, and/or .selects but can't narrow it down as the complicated structure is confusing me.
There are many markets in events, and there are many events as well. A sample of the structure (tried to edit it for brevity):
{"currencyCode"=>"GBP",
"eventTypes"=>[
{"eventTypeId"=>6423,
"eventNodes"=>[
{"eventId"=>28017227,
"event"=>
{"eventName"=>"Philadelphia # Seattle"
},
"marketNodes"=>[
{"marketId"=>"1.128274650",
"description"=>
{"marketName"=>"Moneyline"}
},
{"marketId"=>"1.128274625",
"description"=>
{"marketName"=>"Winning Margin"}
}}}]},
{"eventId"=>28018251,
"event"=>
{"eventName"=>"Arkansas # Mississippi State"
},
"marketNodes"=>[
{"marketId"=>"1.128299882",
"description"=>
{"marketName"=>"Under/Over 60.5pts"}
},
{"marketId"=>"1.128299881",
"description"=>
{"marketName"=>"Moneyline"}
}}}]},
{"eventId"=> etc....
Tried all kinds of things, for example,
markets = json["eventTypes"].first["eventNodes"].map {|e| e["marketNodes"].map { |e| e["marketId"] } if (e["marketNodes"].map {|e| e["marketName"] == 'Moneyline'})}
markets.flatten
# => yields every marketId not every marketId with marketName of 'Moneyline'
Getting a simple array with every marketId from Moneyline markets with no other information is sufficient. Using Rails methods is fine too if preferred.
Sorry if my editing messed up the syntax. Here's the source. It looks like this only with => instead of : after parsing the JSON.
Thank you!
I love nested maps and selects :D
require 'json'
hash = JSON.parse(File.read('data.json'))
moneyline_market_ids = hash["eventTypes"].map{|type|
type["eventNodes"].map{|node|
node["marketNodes"].select{|market|
market["description"]["marketName"] == 'Moneyline'
}.map{|market| market["marketId"]}
}
}.flatten
puts moneyline_market_ids.join(', ')
#=> 1.128255531, 1.128272164, 1.128255516, 1.128272159, 1.128278718, 1.128272176, 1.128272174, 1.128272169, 1.128272148, 1.128272146, 1.128255464, 1.128255448, 1.128272157, 1.128272155, 1.128255499, 1.128272153, 1.128255484, 1.128272150, 1.128255748, 1.128272185, 1.128278720, 1.128272183, 1.128272178, 1.128255729, 1.128360712, 1.128255371, 1.128255433, 1.128255418, 1.128255403, 1.128255387
Just for fun, here's another possible answer, this time with regexen. It is shorter but might break depending on your input data. It reads the json data directly as String :
json = File.read('data.json')
market_ids = json.scan(/(?<="marketId":")[\d\.]+/)
market_names = json.scan(/(?<="marketName":")[^"]+/)
moneyline_market_ids = market_ids.zip(market_names).select{|id,name| name=="Moneyline"}.map{|id,_| id}
puts moneyline_market_ids.join(', ')
#=> 1.128255531, 1.128272164, 1.128255516, 1.128272159, 1.128278718, 1.128272176, 1.128272174, 1.128272169, 1.128272148, 1.128272146, 1.128255464, 1.128255448, 1.128272157, 1.128272155, 1.128255499, 1.128272153, 1.128255484, 1.128272150, 1.128255748, 1.128272185, 1.128278720, 1.128272183, 1.128272178, 1.128255729, 1.128360712, 1.128255371, 1.128255433, 1.128255418, 1.128255403, 1.128255387
It outputs the same result as the other answer.

How to build an array comprised of two others using only particular elements of each?

I writing a little program to generate some bogus top-ten sales numbers for book sales. I'm trying to do this in as compact a fashion as possible and do it without using MySQL or another DB.
I have written out what I want to happen. I've created a bogus catalog array and a bogus sales array corresponding sales to the index of the catalog entries. That part all works great.
I want to create a third array that includes all the titles from the catalog array with the sales numbers from the sales array, like a join in a DB, but without any DB. I can't figure out how to do that part of it though. I think once I have it in there I can sort it the way I want it, but making that third array is killing. I cannot figure out what I'm doing wrong or how to do it right.
So given the following code:
require 'random_word'
class BestOnline
def initialize
#catalog = Array.new
#sales = Array.new
#topten = Array.new
inventory = rand(50) + 10
days = rand(1..50)
now = Time.now
yesterday = now - 86400
saleshistory = now - (days * 86400)
(1..inventory).each do
#catalog << {
:title => "#{RandomWord.adjs.next.capitalize} #{RandomWord.nouns.next.capitalize}",
:price => rand(5.99..29.99).round(2)}
end
(0..days).each do
#sales << {
:id => rand(0..#catalog.count),
:salescount => rand(0..24),
:date => rand(saleshistory..now) }
end
end
def bestsellers
#sales.each do
# THIS DOESNT WORK AND I'M STUCK AS HOW TO FIX IT.
# #topten << {
# :title => #catalog[:id],
# :salescount => #sales[:salescount]
# }
end
puts #topten.group_by{ |tt| tt[:salescount]}.sort_by{ |k,v| -k}.first(10)
end
end
BestOnline.new.bestsellers
How can I create a third array that contains the titles and number of sales and output the result of the top-ten books sold?
Try this out:
def bestsellers
#sales.each do |sale|
#topten << {
title: #catalog[sale[:id]][:title],
salescount: sale[:salescount] }
end
#topten.sort! { |x, y| y[:salescount] <=> x[:salescount] }
puts #topten.first(10)
end
I suggest you write:
def bestsellers(sales)
sales.max_by(10) { |h| h[:salescount][:salescount]] }
end
puts bestsellers(sales)
Enumerable#max_by was permitted to have an argument in Ruby v2.2.
There are several problems with the way you've structured your code. Now that you have running code (by incorporating #fbonds66's answer), I suggest you post it at SO's sister-site Code Review. The purpose of CR is to suggest improvements to working code. If you read through some of the questions and answers there I think you will be impressed.
I was doing the dereferencing wrong trying to build the 3rd array of the 1st two:
#sales.each do |sale|
#topten << {
:title => #catalog[sale[:id]][:title],
:salescount => sale[:salescount]
}
end
I needed to work on the hash returned from .each as |sale| and use correct syntax to get what I was after from the other arrays.

Matlab string manipulation

I need help with matlab using 'strtok' to find an ID in a text file and then read in or manipulate the rest of the row that is contained where that ID is. I also need this function to find (using strtok preferably) all occurrences of that same ID and group them in some way so that I can find averages. On to the sample code:
ID list being input:
(This is the KOIName variable)
010447529
010468501
010481335
010529637
010603247......etc.
File with data format:
(This is the StarData variable)
ID>>>>Values
002141865 3.867144e-03 742.000000 0.001121 16.155089 6.297494 0.001677
002141865 5.429278e-03 1940.000000 0.000477 16.583748 11.945627 0.001622
002141865 4.360715e-03 1897.000000 0.000667 16.863406 13.438383 0.001460
002141865 3.972467e-03 2127.000000 0.000459 16.103060 21.966853 0.001196
002141865 8.542932e-03 2094.000000 0.000421 17.452007 18.067214 0.002490
Do not be mislead by the examples I posted, that first number is repeated for about 15 lines then the ID changes and that goes for an entire set of different ID's, then they are repeated as a whole group again, think [1,2,3],[1,2,3], the main difference is the values trailing the ID which I need to average out in matlab.
My current code is:
function Avg_Koi
N = evalin('base', 'KOIName');
file_1 = evalin('base', 'StarData');
global result;
for i=1:size(N)
[id, values] = strtok(file_1);
result = result(id);
result = result(values)
end
end
Thanks for any assistance.
You let us guess a lot, so I guess you want something like this:
load StarData.txt
IDs = { 010447529;
010468501;
010481335;
010529637;
010603247;
002141865}
L = numel(IDs);
values = cell(L,1);
% Iteration through all arrays and creating an cell array with matrices for every ID
for ii=1:L;
ID = IDs{ii};
ID_first = find(StarData(:,1) == ID,1,'first');
ID_last = find(StarData(:,1) == ID,1,'last');
values{ii} = StarData( ID_first:ID_last , 2:end );
end
When you now access the index ii=6 adressing the ID = 002141865
MatrixOfCertainID6 = values{6};
you get:
0.0038671440 742 0.001121 16.155089 6.2974940 0.001677
0.0054292780 1940 0.000477 16.583748 11.945627 0.001622
0.0043607150 1897 0.000667 16.863406 13.438383 0.001460
0.0039724670 2127 0.000459 16.103060 21.966853 0.001196
0.0085429320 2094 0.000421 17.452007 18.067214 0.002490
... for further calculations.

Resources