Inheritance of rows in SQL - sql

I have a table of rows, here is a sample (as hashes):
{:id => 8, :n => 774, :inherits_from => [], :properties => ["a", "b", "c"]}
{:id => 9, :n => 915, :inherits_from => [8], :properties => ["d"]}
{:id => 10, :n => 754, :inherits_from => [1, 2], :properties => []}
{:id => 11, :n => 774, :inherits_from => [10], :properties => ["a", "b"]}
The idea is that properties can be inherited from other rows:
table[:id => 9] #=> {:id => 9, :n => 915, :inherits_from => [8], :properties => ["a", "b", "c", "d"]}
Rows can have one or several inheritances, or can have none. There can be many levels of inheritance, i.e., 5 inherits from 2 and 3, 7 inherits from 5 (which means that 7 inherits from 5, but also from 2 and 3, because 5 inherits from them) etc.
I know I could do it with brute force (i.e., for every queried row check if it inherits from somewhere). Also, I could use Class.new to generate a class for each row and set inheritances (I use ruby), but there will be thousands of rows.
Any advise on how could I do it in the most efficient way?

Related

How to group by a key and sum other keys with Ramda?

Suppose I have an array of objects like this:
[
{'prop_1': 'key_1', 'prop_2': 23, 'prop_3': 45},
{'prop_1': 'key_1', 'prop_2': 56, 'prop_3': 10},
{'prop_1': 'key_2', 'prop_2': 10, 'prop_3': 5},
{'prop_1': 'key_2', 'prop_2': 6, 'prop_3': 7}
]
I would like to group by the first property and sum the values of the other properties, resulting in an array like this:
[
{'prop_1': 'key_1', 'prop_2': 79, 'prop_3': 55},
{'prop_1': 'key_2', 'prop_2': 16, 'prop_3': 12}
]
What is the correct way to do this using Ramda?
I have attempted to use the following:
R.pipe(
R.groupBy(R.prop('prop_1')),
R.values,
R.reduce(R.mergeWith(R.add), {})
)
But this sums also the value of 'prop_1'.
You'll need to map the groups, and then reduce each group. Check the values of the currently merged keys. If the value is a Number add the values. If not just return the 1st value:
const { pipe, groupBy, prop, values, map, reduce, mergeWith, ifElse, is, add, identity } = R
const fn = pipe(
groupBy(prop('prop_1')),
values,
map(reduce(
mergeWith(ifElse(is(Number), add, identity)),
{}
))
)
const data = [{"prop_1":"key_1","prop_2":23,"prop_3":45},{"prop_1":"key_1","prop_2":56,"prop_3":10},{"prop_1":"key_2","prop_2":10,"prop_3":5},{"prop_1":"key_2","prop_2":6,"prop_3":7}]
const result = fn(data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
If and only if you know the keys in advance, you could opt for something "simpler" (of course YMMV):
reduceBy takes a function similar to Array#reduce
Use prop('prop_1') as a "group by" clause
(It should be relatively straightforward to extract the values out of the object to get the final array.)
console.log(
reduceBy
( (acc, obj) =>
({ prop_1: obj.prop_1
, prop_2: acc.prop_2 + obj.prop_2
, prop_3: acc.prop_3 + obj.prop_3
})
// acc initial value
, { prop_1: ''
, prop_2: 0
, prop_3: 0
}
// group by clause
, prop('prop_1')
, data
)
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.min.js"></script>
<script>const {reduceBy, prop} = R;</script>
<script>
const data = [{'prop_1': 'key_1', 'prop_2': 23, 'prop_3': 45}, {'prop_1': 'key_1', 'prop_2': 56, 'prop_3': 10}, {'prop_1': 'key_2', 'prop_2': 10, 'prop_3': 5}, {'prop_1': 'key_2', 'prop_2': 6, 'prop_3': 7}];
</script>
This answer is not as simple as that from Ori Drori. And I'm not sure whether that's a good thing. This seems to more closely fit the requirements, especially if "and sum the values of the other properties" is a simplification of actual requirements. This tries to keep the key property and then combine the others based on your function
const data = [
{'prop_1': 'key_1', 'prop_2': 23, 'prop_3': 45},
{'prop_1': 'key_1', 'prop_2': 56, 'prop_3': 10},
{'prop_1': 'key_2', 'prop_2': 10, 'prop_3': 5},
{'prop_1': 'key_2', 'prop_2': 6, 'prop_3': 7}
]
const transform = (key, combine) => pipe (
groupBy (prop (key)),
map (map (omit ([key]))),
map (combine),
toPairs,
map (([key, obj]) => ({prop_1: key, ...obj}))
)
console .log (
transform ('prop_1', reduce(mergeWith (add), {})) (data)
)
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script> const {pipe, groupBy, prop, map, omit, reduce, mergeWith, add, toPairs, lift, merge, head, objOf, last} = R </script>
If you have a fetish for point-free code that last line could be written as
map (lift (merge) (pipe (head, objOf(key)), last))
But as we are already making points of key and obj, I see no reason. Yes, we could change that, but I think it would become pretty ugly code.
There might well be something to be said for a more reduce-like version, where instead of passing such a combine function, we pass something that combines two values as well as a way to get the initial value. That's left as an exercise for later.

Elixir best data structure for testing comparison

I have two array outputs where I need to iterate over each struct, and compare the counts where the source's match. The comparison needs to be less than or equal to. My output sources look like this:
output_1: [%{source: "facebook", count: 3}, %{count: 1, source: "linkedin"}]
output_2: [%{source: "facebook", count: 2}, %{count: 1, source: "linkedin"}]
Whats the best data structure to implement in order to make the Enumerables easiest and most efficient to compare?
If your order isn't guaranteed, my preferred way is to turn the reference list into a map and compare things by source.
iex> output_1 = [%{source: "facebook", count: 3}, %{count: 1, source: "linkedin"}]
[%{count: 3, source: "facebook"}, %{count: 1, source: "linkedin"}]
iex> output_2 = [%{source: "facebook", count: 2}, %{count: 1, source: "linkedin"}]
[%{count: 2, source: "facebook"}, %{count: 1, source: "linkedin"}]
iex> limits = Map.new(output_1, &{&1.source, &1.count})
%{"facebook" => 3, "linkedin" => 1}
iex> Enum.all?(output_2, & &1.count <= limits[&1.source])
true
Your current output format should be very efficient with the following code. You didn't say what you expected your output to be, nor in which direction the comparison should be done: output2 <= output1 or output1 <= output2, so I'm assuming a list of booleans and output1 <= output2:
defmodule A do
def compare([%{count: count1}|maps1], [%{count: count2}|maps2]) do
[count1 <= count2 | compare(maps1, maps2) ]
end
def compare([], []), do: []
end
The following does the same thing and is easier to come up with and understand:
defmodule A do
def compare(list1, list2), do: _compare(list1, list2, [])
defp _compare([%{count: count1}|maps1], [%{count: count2}|maps2], acc) do
_compare(maps1, maps2, [count1 <= count2 | acc])
end
defp _compare([], [], acc) do
Enum.reverse(acc)
end
end
In iex:
~/elixir_programs$ iex a.ex
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> out1 = [%{source: "facebook", count: 3}, %{count: 1, source: "linkedin"}]
[
%{count: 3, source: "facebook"},
%{count: 1, source: "linkedin"}
]
iex(2)> out2 = [%{source: "facebook", count: 2}, %{count: 1, source: "linkedin"}]
[
%{count: 2, source: "facebook"},
%{count: 1, source: "linkedin"}
]
iex(3)> A.compare(out1, out2)
[false, true]
If instead, you need the result to be a single boolean, i.e. the facebook count is less than or equal to AND the linkedin count is less than or equal to, you can change the accumulator:
defmodule A do
def compare(list1, list2), do: _compare(list1, list2, true)
defp _compare([%{count: count1}|maps1], [%{count: count2}|maps2], true) do
_compare(maps1, maps2, count1 <= count2)
end
defp _compare(_, _, false), do: false #If you find a false comparison, stop and return false
defp _compare([], [], _), do: true
end
In iex:
iex(22)> c "a.ex"
warning: redefining module A (current version defined in memory)
a.ex:1
[A]
iex(23)> A.compare(out1, out2)
false
This also works:
defmodule A do
def compare(list1, list2) do
List.first(list1)[:count] <= List.first(list2)[:count]
and
List.last(list1)[:count] <= List.last(list2)[:count]
end
end
Whats the best data structure to implement in order to make the Enumerables easiest and most efficient to compare?
Otherwise, I would nominate a keyword list like this:
[facebook: 3, linkedin: 1]
[facebook: 2, linkedin: 1]
The easiest would probably to be use Enum.zip/2 with Enum.all?/2. Something like the following should work
output_1 = Enum.sort(output_1, fn a, b -> a.source <= b.source end)
output_2 = Enum.sort(output_2, fn a, b -> a.source <= b.source end)
output_1
|> Enum.zip(output_2)
|> Enum.all?(fn a, b -> a.count == b.count end)

thinking sphinx search has_many not working no error

model
class Person < ActiveRecord::Base
attr_accessible :alignment, :description, :first_name, :last_name
has_many :roles #table roles with active as one of the field with value equal t or f (boolean)
end
class Role < ActiveRecord::Base
attr_accessible :active, :organization_id, :person_id, :position_id
belongs_to :person
belongs_to :organization
belongs_to :position
end
person_index.rb
ThinkingSphinx::Index.define :person, :with => :active_record do
#Fields
indexes last_name, :sortable => true
indexes first_name, :sortable => true
indexes alignment
#indexes role(:active), :as => :active
indexes role.active, :as => :active
#Attributes
has created_at, updated_at
has professions(:id), :as => :profession_ids
has positions(:id), :as => :position_id
has organizations(:id), :as => :organization_id
end
people_controller
filters = {}
filters[:profession_ids] = params[:profession_ids] if params[:profession_ids].present?
filters[:organization_id] = params[:organization_id] if params[:organization_id].present?
filters[:position_id] = params[:position_id] if params[:position_id].present?
filters[:active_ids] = role if params[:active].present? #added
#people = Person.search " #{params[:lastname]} #{params[:firstname]} #{params[:alignmemt]}",
:with => filters,
:star => true,
:condition => {:alignment => params[:alignment], :active => params[:active]},
:order => 'last_name ASC, first_name ASC',
:page => params[:page],
:per_page => 20
When i search active and/or alignment it is not filtering result and doesn't give me error. these are both string field, alignment is in the people table and active is in another table (roles)
Why? What am i missing?
update
Tried the recommended solution for active on this question and same result...
person_index.rb
ThinkingSphinx::Index.define :person, :with => :active_record do
#Fields
indexes last_name, :sortable => true
indexes first_name, :sortable => true
indexes alignment
#Attributes
has created_at, updated_at
has professions(:id), :as => :profession_ids
has positions(:id), :as => :position_id
has organizations(:id), :as => :organization_id
has roles.active, :as => :active_ids
end
people_controller
def index
#role = Role.find_by_active(params[:active]) #ADDED
filters = {}
filters[:profession_ids] = params[:profession_ids] if params[:profession_ids].present?
filters[:organization_id] = params[:organization_id] if params[:organization_id].present?
filters[:position_id] = params[:position_id] if params[:position_id].present?
#people = Person.search " #{params[:lastname]} #{params[:firstname]} #{params[:alignmemt]}",
:with => filters,
:star => true,
:condition => {:alignment => params[:alignment], :active_ids => #role}, #CHANGED
:order => 'last_name ASC, first_name ASC',
:page => params[:page],
:per_page => 20
but still have same result... WHY?
controller updated after Pat answer
def index
if params[:active].present?
role = Array.new
rolepid = Array.new
role = Role.find_all_by_active(params[:active])
role.each do |num|
puts num.person_id
rolepid << num.person_id #get all the person id whith the params[:active]
end
end
filters = {}
filters[:profession_ids] = params[:profession_ids] if params[:profession_ids].present?
filters[:organization_id] = params[:organization_id] if params[:organization_id].present?
filters[:position_id] = params[:position_id] if params[:position_id].present?
filters[:active_ids] = rolepid if params[:active].present?
#people = Person.search " #{params[:lastname]} #{params[:firstname]} #{params[:alignent]}",
#:classes => [Person, Role],
:with => filters,
:star => true,
:condition => {:alignment => params[:alignment]},
:order => 'last_name ASC, first_name ASC',
:page => params[:page],
:per_page => 20
But now it is looking for active in people table when it should look in roles table. So i added #:classes => [Person, Role], but no luck....
Role Load (0.7ms) SELECT "roles".* FROM "roles" WHERE "roles"."active" = 'f'
Sphinx Query (0.7ms) SELECT * FROM `person_core` WHERE `active_ids` IN (304, 34, 306, 308, 334, 295, 344, 348, 352, 354, 365, 367, 308, 429, 468, 9, 544, 590, 609, 110, 1643, 1652, 1653, 1655, 1669, 628, 1687, 1691, 1709) AND `sphinx_deleted` = 0 ORDER BY `last_name` ASC, first_name ASC LIMIT 0, 20
Sphinx Found 0 results
So i change in the controller
filters[:active_ids] = rolepid if params[:active].present?
to
filters[:id] = rolepid if params[:active].present?
Since rolepid is an array of integer with the person ids.
But Sphinx is just looking for 4 ids that are not in rolepid... I am confused :|
Parameters: {"utf8"=>"✓", "firstname"=>"", "lastname"=>"", "alignment"=>"", "organization_id"=>"", "position_id"=>"", "active"=>"f", "commit"=>"Search"}
Role Load (0.8ms) SELECT "roles".* FROM "roles" WHERE "roles"."active" = 'f'
Sphinx Query (0.6ms) SELECT * FROM `person_core` WHERE `id` IN (304, 34, 306, 308, 334, 295, 344, 348, 352, 354, 365, 367, 308, 429, 468, 9, 544, 590, 609, 110, 1643, 1652, 1653, 1655, 1669, 628, 1687, 1691, 1709) AND `sphinx_deleted` = 0 ORDER BY `last_name` ASC, first_name ASC LIMIT 0, 20
Sphinx Found 4 results
Person Load (0.4ms) SELECT "people".* FROM "people" WHERE "people"."id" IN (84, 1, 61, 50)
Why is it not returning the 29 records from rolepid array ?
filtering for alignment IS working. thanks for catching the misspelled word.
If you're using active_ids as an attribute (which, if it's integers, is certainly appropriate), then it should be a filter in the :with option, not in the :conditions option.
I'm not sure if this is related, but it's worth noting you've misspelled alignment in the query string (you've got alignmemt instead).

Calculate Percent of total from scoped records and display virtual attributes (Rails / Active Record)

Create a scope, maybe something like this..
scope :mv, select('*,quantity*market_price as market_value, quantity*market_price/sum(quantity*market_price) as percent')
that creates two virtual attributes, market_value and percent. The problem I am having is creating the percent with the sum() included. If I add sum(), the scope returns one record.
I need to calculate the percent of market_value relative to the total market value of the scoped record set.
example:
1, market_value: 100, percent: .10
2, market_value: 100, percent: .10
3, market_value: 100, percent: .10
4, market_value: 100, percent: .10
5, market_value: 100, percent: .10
6, market_value: 500, percent: .50
Total is 1000
however, if I scope it to where market_value < 6, I should see
1, market_value: 100, percent: .20
2, market_value: 100, percent: .20
3, market_value: 100, percent: .20
4, market_value: 100, percent: .20
5, market_value: 100, percent: .20
Total 500
How can I accomplish this?
I created a self.pct method but problem with the self.pct method is that it needs to be run after all the scopes. if rescoped, the solution is wrong
So far,
class Position < ActiveRecord::Base
attr_accessible :account_id, :account_type, :market_price, :quantity, :report_date, :symbol
scope :long_only, where(:account_type => 'L')
scope :short_only, where(:account_type=>"S")
scope :equity_only, :conditions => ["symbol <> 'USD'"]
scope :mv, select('*,quantity*market_price as market_value, quantity*market_price/sum(quantity*market_price) as percent')
scope :mv1, lambda{|total| select(total) }
#the problem with the self.pct method is that it needs to be run after all the scopes. if rescoped, the solution is wrong
def self.pct
string="*,(quantity*market_price) as market_value, (market_price*quantity/#{sum_market_value}) as percent"
mv1(string)
end
def market_value
self.market_price*self.quantity
end
def self.sum_market_value
sum('quantity*market_price')
end
end
I don't know if there's a way to do this in a single query, but we can get it in two queries:
require 'active_record'
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
ActiveRecord::Schema.define do
self.verbose = false
create_table :positions do |t|
t.integer :quantity
t.integer :market_price
end
end
class Position < ActiveRecord::Base
def self.with_market_value
select "*,
quantity*market_price as market_value,
quantity*market_price/#{total.to_f} as percent"
end
def self.total
select('sum(quantity*market_price) as sum_of_market_values').first.sum_of_market_values
end
end
Position.create! quantity: 25, market_price: 4
Position.create! quantity: 25, market_price: 4
Position.create! quantity: 25, market_price: 4
Position.create! quantity: 25, market_price: 4
Position.create! quantity: 25, market_price: 4
Position.create! quantity: 25, market_price: 20
Position.with_market_value.map { |p| [p.market_value, p.percent] }
# => [[100, 0.1], [100, 0.1], [100, 0.1], [100, 0.1], [100, 0.1], [500, 0.5]]
Position.where('market_price < 10').with_market_value.map { |p| [p.market_value, p.percent] }
# => [[100, 0.2], [100, 0.2], [100, 0.2], [100, 0.2], [100, 0.2]]
# ** NOTE THAT IT EXECUTES EAGERLY **
Position.with_market_value.where('market_price < 10').map { |p| [p.market_value, p.percent] }
# => [[100, 0.1], [100, 0.1], [100, 0.1], [100, 0.1], [100, 0.1]]

Group By Year, Month & then Count in ActiveRecord 3

I'm trying to pull a count of all users created in a year, and month but the following doesn't seem to work as expected.
User.group("YEAR(created_AT), MONTH(created_at)").
count("DISTINCT(id), YEAR(created_at), MONTH(created_at)")
i'm looking for something like
{2011 => {1 => 222, 2 => 333, 4 => 444, 5 => 667 ... }}
but i'm getting
{1 => 222, 2 => 333, 4 => 444, 5 => 667 ... }
Am i missing something, or can ActiveRecord not give me this result in one query?
The count method doesn't work like you think it does. You end up doing this:
select count(distinct(id), year(created_at), month(created_at))
from users
group by year(created_at), month(created_at)
That SELECT clause is pretty dodgy but MySQL will, in its usual sloppy manner, let it through. I think you want this query:
select count(distinct(id)), year(created_at), month(created_at)
from users
group by year(created_at), month(created_at)
I'd probably go straight to select_all like this:
a = User.connection.select_all(%q{
select count(distinct(id)) as c, year(created_at) as y, month(created_at) as m
from users
group by y, m
})
Or you could do it like this:
a = User.connection.select_all(
User.select('count(distinct(id)) as c, year(created_at) as y, month(created_at) as m').
group('y, m')
)
Those will give you an array, a, of Hashes with c, y, and m keys like this:
a = [
{ 'c' => '23', 'y' => '2010', 'm' => '11' },
{ 'c' => '1', 'y' => '2011', 'm' => '1' },
{ 'c' => '5', 'y' => '2011', 'm' => '3' },
{ 'c' => '2', 'y' => '2011', 'm' => '4' },
{ 'c' => '11', 'y' => '2011', 'm' => '8' }
]
Then a bit of data wrangling is all you need to finish the job:
h = a.group_by { |x| x['y'] }.each_with_object({}) do |(y,v), h|
h[y.to_i] = Hash[v.map { |e| [e['m'].to_i, e['c'].to_i] }]
end
# {2010 => {11 => 23}, 2011 => {1 => 1, 3 => 5, 4 => 2, 8 => 11}}