Find intersection of two array of hashes depending upon hash contents - ruby

I get this two array of hashes after performing join
array 1
[#<State id: 1, name: "Alabama">, #<State id: 1, name: "Alabama">, #<State id: 1, name: "Alabama">, #<State id: 1, name: "Alabama">, #<State id: 2, name: "Alaska">, #<State id: 2, name: "Alaska">, #<State id: 4, name: "Arkansas">, #<State id: 4, name: "Arkansas">, #<State id: 4, name: "Arkansas">, #<State id: 6, name: "Colorado">, #<State id: 6, name: "Colorado">, #<State id: 6, name: "Colorado">, #<State id: 11, name: "Georgia">, #<State id: 14, name: "Illinois">, #<State id: 18, name: "Kentucky">, #<State id: 18, name: "Kentucky">, #<State id: 22, name: "Massachusetts">, #<State id: 48, name: "Washington">]
array 2
[#<City id: 1, name: "Abbeville", state_id: 1>, #<City id: 1, name: "Abbeville", state_id: 1>, #<City id: 1, name: "Abbeville", state_id: 1>, #<City id: 4543, name: "Abingdon", state_id: 14>, #<City id: 8282, name: "Accord", state_id: 22>, #<City id: 3808, name: "Acworth", state_id: 11>, #<City id: 6855, name: "Adairville", state_id: 18>, #<City id: 6855, name: "Adairville", state_id: 18>, #<City id: 18895, name: "Adams County", state_id: 6>, #<City id: 4, name: "Addison", state_id: 1>, #<City id: 4, name: "Addison", state_id: 1>, #<City id: 17510, name: "Addy", state_id: 48>, #<City id: 1054, name: "Adona", state_id: 4>, #<City id: 1054, name: "Adona", state_id: 4>, #<City id: 577, name: "Akiachak", state_id: 2>, #<City id: 1056, name: "Alicia", state_id: 4>, #<City id: 583, name: "Ambler", state_id: 2>, #<City id: 2783, name: "Aspen", state_id: 6>]
I want to make a third array from the above two based on the value of state_id in each array
in this case for example
[#, .... and so on
for your help the first two hashes array i got using join query
#states = State.joins("INNER JOIN property_of_interests ON property_of_interests.state_id = states.id").where(:property_of_interests => {:user_id => current_user.id})
#cities = City.joins("INNER JOIN property_of_interests ON property_of_interests.city_id = cities.id").where(:property_of_interests => {:user_id => current_user.id})
can I work on the query itself to get the desired output ?.
I tried something like
`#states.select("#states.name,#cities.name").joins("INNER JOIN #cities ON #cities.state_id = #states.id")`
but it doesnt work.
More Information
states
id, name
cities
id, name, state_id
property_of_interests
id, user_id, state_id, state_name
states has cities
cities belongs to states
states belongs to property_of_interests
cities belongs to property_of_interests
property_of_interests has cities
property_of_interests has states
Desired output like
State Name City Name
Alabama Abbeville
Alabama Abbeville
Alabama Abbeville
....

You would do something like:
City.all.each do |city|
puts "#{city.state.name} #{city.name}"
end
Alternately, as an array:
arr = City.all.map { |c| [c.state.name, c.name] }
Or as an array of hashes:
arr = City.all.map { |c| {state: c.state.name, city: c.name} }
Or to actually answer the question, since you want to start with the properties_of_interest table:
PropertyOfInterest.all.each do |prop|
prop.state.cities.each do |city|
puts prop.state.name, city.name
end
end

Related

How do I remove duplicate items from observable array based on multiple properties?

If I have an array of items like
[
{id: 1, name: 'Sam', gender: 'boy'},
{id: 2, name: 'Mary', gender: 'girl'},
{id: 3, name: 'Sam', gender: 'boy'}
]
Matching on just name and gender, how do I reduce it to the following result?
[
{id: 1, name: 'Sam', type: 'boy'},
{id: 2, name: 'Mary', type: 'girl'}
]
Let try
items$.pipe(map(this.uniqueArray))
uniqueArray(array: any[]): any[] {
return array.filter(
(item, index, self) =>
index === self.findIndex((x) => x.name === item.name)
);
}
https://stackblitz.com/edit/angular-isqjpa?file=src/app/hello.component.ts

Why do aggregating a filtered dataset lose the filters of it?

I have this collection:
// collection
[
{_id: 1, name: 'Luigi', childs: [{name: 'one'}, {name: 'two'}], dad_id: 9]},
{_id: 1, name: 'Mario', childs: [{name: 'four'}, {name: 'five'}], dad_id: 8]},
{_id: 1, name: 'Alessandro', childs: [{name: 'seven'}, {name: 'six'}], dad_id: 9]},
]
and apply this filter to it
result = collection.find({ dad_id: 9 })
Then I want to aggregate the results and get all the childs singularly, I start with unwinding them
(then I 'll make a projection, etc..) but I already encounter a behavior that I do not understand:
the result contains also the documents with dad_id is 8, even if they were already excluded by my query.
result.aggregate([
{ "$unwind"=> "$childs" },
]).each do |e| ... end
// => [
{_id: 1, name: 'Luigi', childs: {name: 'one'}, dad_id: 9]},
{_id: 1, name: 'Luigi', childs: {name: 'two'}, dad_id: 9]},
{_id: 1, name: 'Luigi', childs: {name: 'five'}, dad_id: 8]},
{_id: 1, name: 'Luigi', childs: {name: 'four'}, dad_id: 8]},
{_id: 1, name: 'Luigi', childs: {name: 'seven'}, dad_id: 9]},
{_id: 1, name: 'Luigi', childs: {name: 'six'}, dad_id: 9]},
]
What am I missing?
You can not chain input from one query to another query like that.
Either use search query ex. Model.find(id) or aggregation framework.
Aggregation framework provides you the functionality to create a pipeline (ex. match,unwind,lookup,project).
To utilize mongodb indexing always try to use "$match" first in the pipeline
match = { "$match" => { "dad_id" =>9} }
unwind = {"$uwind"=>"$childs"}
pipeline = [match,unwind]
collection.aggregate(pipeline).each do |obj|
end

How to use .uniq with hashes for unique pairs?

Lets say I have an array with several hashes of emails and names. For example I have something like this:
foo = [{id: 1, name: 'Eric Cartman', email: 'eric#southpark.com'},
{id: 2, name: 'Eric Cartman', email: 'cartmanfamily#gmail.com'},
{id: 3, name: "Cartman's mom", email: 'cartmanfamily#gmail.com'},
{id: 4, name: 'Eric Cartman', email: 'eric#southpark.com'}]
How can I use .uniq to return unique values based on the combination of name and email? For example I want to return something like this:
[{id: 1, name: 'Eric Cartman', email: 'eric#southpark.com'},
{id: 2, name: 'Eric Cartman', email: 'cartmanfamily#gmail.com'},
{id: 3, name: "Cartman's mom", email: 'cartmanfamily#gmail.com'}]
foo.uniq should work just fine.
Since
{name: "cartman", email: "cartman#sp.com"} == {name: "cartman", email: "cartman#sp.com"} # => True
{name: "stan", email: "stan#sp.com"} == {name: "cartman", email: "cartman#sp.com"} # => False
The == operator check if every field of the hash have the same values. So .uniq will work how you want it to work!
If there is more than only the email and name field you should use the uniq method with a block:
foo.uniq { |x| [x[:name], x[:email]] }
It will keep only the uniq combination of the name and email.
Hope it helped, happy ruby coding!
Array#uniq takes a block:
foo = [{id: 1, name: 'Eric Cartman', email: 'eric#southpark.com'},
{id: 2, name: 'Eric Cartman', email: 'cartmanfamily#gmail.com'},
{id: 3, name: "Cartman's mom", email: 'cartmanfamily#gmail.com'},
{id: 4, name: 'Eric Cartman', email: 'eric#southpark.com'}]
bar = foo.uniq {|h| [h[:name], h[:email]] }
bar == [{id: 1, name: 'Eric Cartman', email: 'eric#southpark.com'},
{id: 2, name: 'Eric Cartman', email: 'cartmanfamily#gmail.com'},
{id: 3, name: "Cartman's mom", email: 'cartmanfamily#gmail.com'}] #=> true
Per the documentation, "If a block is given, it will use the return value of the block for comparison."

Elastic search : how to query to return certain number products for different users

I have a collection of products which belong to few users, (the system is with ElasicSearch(ES), MySQL, Scala and ES Play Framework APIs link):
[
{ id: 1, user_id: 'jason', product: [...] },
{ id: 2, user_id: 'mike', product: [...] },
{ id: 3, user_id: 'mike', product: [...] },
{ id: 4, user_id: 'dan', product: [...] },
{ id: 5, user_id: 'bill', product: [...] },
{ id: 6, user_id: 'mike', product: [...] },
{ id: 7, user_id: 'dan', product: [...] },
{ id: 8, user_id: 'bill', product: [...] },
{ id: 9, user_id: 'mike', product: [...] },
{ id: 10, user_id: 'dan', product: [...] },
{ id: 11, user_id: 'bill', product: [...] },
...
]
I'd like to retrieve some certain number (for example, top 2 with highest matching score) products of best matching document based upon the user's id:
[
{ id: 2, user_id: 'mike', product: [...], _score: 100},
{ id: 3, user_id: 'mike', product: [...], _score: 95},
{ id: 4, user_id: 'dan', product: [...], _score: 90},
{ id: 5, user_id: 'bill', product: [...], _score: 80},
{ id: 7, user_id: 'dan', product: [...], _score: 70},
{ id: 8, user_id: 'bill', product: [...], _score: 65},
...
]
I tried term facets on user_id, but I cannot find equal number products for each user currently, for example,
[
{ id: 2, user_id: 'mike', product: [...], _score: 100},
{ id: 3, user_id: 'mike', product: [...], _score: 95},
{ id: 4, user_id: 'dan', product: [...], _score: 90},
{ id: 5, user_id: 'bill', product: [...], _score: 80},
{ id: 6, user_id: 'mike', product: [...], _score: 75},
...
]
Term facets pseudo code:
/** query type is com.github.cleverage.elasticsearch.ScalaHelpers.IndexResults[Product]
* filtered is matching requirement filter, i.e. including keyword "fashion"
* limit is the size of returned users with matching document, i.e. 10
* finalQuery return 5 unique users based on tmpQuery result with 10 users
* each user has 2 products finally
*/
tmpQuery = query.withBuilder(filtered).withSize(limit)
finalQuery = tmpQuery.addFacet(FacetBuilders.termsFacet("userId").field("user_id").size(5))
How to ensure everyone has 2 products, rather than mike has 3, dan has 1 and bill has 1?
I mean, addFacet doesn't work now because finalQuery is based on tmpQuery, and tmpQuery returns 10 results which are more from mike due to higher matching score, how to update tmpQuery to reach the limitation of 2?)
Term facet cannot ensure unique users, it only return most frequent users. Actually, in this case, have to match products first, then retrieve its user_id then, so it cannot get users first and then their products.
Appreciate.

How do I get a nested hash from a join?

I'm using Sequel to read rows from a SQLite database. No writing is necessary.
I am using a basic join. Consider this:
db = Sequel.sqlite
db[:user].join_table(:left, :photos, user_id: :user_id)
This joins the photo table on the user table (keeping the users that have no photos).
However this multiplies all the lines of the user by the number of its photos:
[
{user_id: 1, name: 'User1', photo_id: 1, photo_name: 'profile.jpg'}
{user_id: 1, name: 'User1', photo_id: 2, photo_name: 'cat.jpg'}
{user_id: 1, name: 'User1', photo_id: 3, photo_name: 'dog.jpg'}
{user_id: 2, name: 'User2', photo_id: 4, photo_name: 'profile.jpg'}
{user_id: 2, name: 'User2', photo_id: 5, photo_name: 'profile.jpg'}
{user_id: 3, name: 'User3', photo_id: nil, photo_name: nil}
]
I would like to have all the photo attributes nested under one key, like this:
[
{user_id: 1, name: 'User1', photos: [{photo_id: 1, photo_name: 'profile.jpg'}, {photo_id: 2, photo_name: 'cat.jpg'}, {photo_id: 3, photo_name: 'dog.jpg'}]}
{user_id: 2, name: 'User2', photos: [{photo_id: 4, photo_name: 'profile.jpg'}, {photo_id: 5, photo_name: 'profile.jpg'}]},
{user_id: 3, name: 'User3', photos: [] }
]
How can I achieve this "mini-relation"? Do I have to specify a model?

Resources