Get grouped values by multiple ids in ActiveRecord - mysql

Sorry for bad English ))
I have an array of ids in my ruby code.
Example:
[[10], [], [10, 1, 3], []]
Can I load User model from MySQL table users in one query by saving grouping?
Example:
[[#<User id=10>], [], [#<User id=10>, #<User id=1>, #<User id=3>], []]
Environment: Ruby 2.5.1 | Rails 5 | MySQL
One of found solution:
I can flat my array of ids and load my model by that array into hash:
hash = User.where(id: array.flatten).index_by(&:id)
Then, during iterating, through array I can load my objects from hash in the right order like that:
array.each do |ids|
users = ids.map { |id| hash[id] }
# do smth
end

This is simple: use flatten method for array:
ids = [[123], [], [123, 1, 3, 4], [70, 80]]
user_ids = ids.flatten.reject(&:blank?).uniq
users = User.where(id: user_ids)
edited:
not optimal (recursive) method for your need:
def map_users_by_id(ids_array:, users:)
result = []
ids_array.each do |array_element|
if (array_element).is_a? Array
result << map_users_by_id(ids_array: array_element, users: users)
else
result << users[array_element]
end
end
return result
end
ids = [[123], [], [123, 1, 3, 4], [70, 80]]
user_ids = ids.flatten.reject(&:blank?).uniq
users = Hash[User.where(id: user_ids).map{|user|[user.id, user]}]
result = map_users_by_id(ids_array: ids, users: users)

Related

MySQL merge json field with new data while removing duplicates, where the json values are simple scalar values

Suppose that I have a MySQL table with a JSON field that contains only numbers, like this (note: using MySQL 8):
CREATE TABLE my_table (
id int,
some_field json
);
Sample data:
id: 1
some_field: [1, 2, 3, 4, 5]
id: 2
some_field: [3, 6, 7]
id: 3
some_field: null
I would like to merge another array of data with the existing values of some_field, while removing duplicates. I was hoping that this might work, but it didn't:
update my_table set some_field = JSON_MERGE([1, 2, 3], some_field)
The result of this would be:
id: 1
some_field: [1, 2, 3, 4, 5]
id: 2
some_field: [1, 2, 3, 6, 7]
id: 3
some_field: [1, 2, 3]
Considering you have 3 records in your table and you want to merge 1 and 2 as mentioned in your example.
I hope JavaScript is suitable to follow through for you.
// Get both the records
const records = db.execute(“SELECT id, some_field FROM my_table WHERE id=1 OR id=2”);
// You get both the rows.
// Merging row1, you can either use the Set data structure if you’re dealing with numbers like your example, or you could loop using a map and use the spread operator if using JSON. Since your object is an array, I’ll just be explaining to merge 2 arrays.
records[0].some_field = Array.from(new Set(records[0].some_field + record[1].some_field))
// Same for second record.
records[1].some_field = Array.from(new Set(records[0].some_field + record[1].some_field))
// Now update both the records in the database one by one.

Django. How i can get list of Users with a duplicate?

i have array like this: [1, 1, 1, 2, 3]. How i can get users with a duplicate? For example this query return list without duplicate
list= User.objects.filter(id__in=[1, 1, 1, 2, 3])
for example it will be users with id's:
1,
2,
3
but i need list of users like this:
1,
1,
1,
2,
3
list = []
for x in [1, 1, 1, 2, 3]:
list.append(User.objects.filter(id=x)
It this what you mean? I don't quite understand the spacing.
Get your queryset sorted in the right order. .order_by('id) for ascending by id (which may be the default anyway). Then iterate over the queryset with code to make extra operations with the same object (or a copy thereof) as dictated by the list of IDs.
idlist = [1, 1, 1, 2, 3]
queryset = User.objects.filter(id__in = idlist ).order_by('id')
for object in queryset:
for _ in range( idlist.count( object.id))
do_something_with( object)
Note, this is only one DB call (one queryset), unlike the accepted answer which does one DB query for each element in the id list. Not good.

Retrive all the value that satisfy the condition of first table

I have two tables users and location. I need to join both tables
what i need is get all the area number of all the users which are present in the user table.
ie user 1 has 3 entries in the second table so i need to join the table in such a way that is,
id1 = 1
area = 2,3
area 2 is repeating so do not include it twice
i tried the join but now getting the correct way to doing it.
What i tried?
$location = User::
join('addresses','users.id1','=','addresses.id1') ->select('users.id1','addresses.area')
->get();
Expected Output
User 1 -> area ->2,3
Here are the two ways to do this.
Firstly you can use Laravel relationship:-
In your model User create relationship:-
function addresses()
{
return $this->hasMany(Address::class, 'id1', 'id1');
}
Now in your User controller you can get User addresses (areas) like this
$users = User::with('addresses')->get();
dd($users->toArray());
This will print something like this
[
{
id1: 1,
name: abaa
pwd: 12345
addresses: [
{
id2: 1,
id1: 1,
area: 2
},
{
id2: 2,
id1: 1,
area: 3
},
{
id2: 3,
id1: 1,
area: 3
}
]
},
{
...
}
]
Second you can use Laravel relationship:-
$builder = new User;
$builder->join('addresses','users.id1','=','addresses.id1')
->selectRaw("users.*, GROUP_CONCAT(DISTINCT addresses.area SEPARATOR ',') as distinct_areas")
->groupBy("users.id1")
->get();
This query will give you result something like this
[
{
id1: 1,
name: abaa,
pwd: 12345,
distinct_areas: 2,3
},
{
...
}
]
I think this will help you.

pluck based on common attribute (rails)

I have a table Followership, in which every record has attributes user_id and follower_id.
I can do something like this:
Followership.limit(10).pluck('user_id, follower_id)
But this will give me result like this [[1,A][2,B],[3,C],[4,A],[1,B][2,D]]
I want to convert the above array in such a way that all arrays with same user_ids should be merged in following form [user_id, FOLLOWER_ID(S)]
i.e;
[[1,[A,B]][2,[B,D]],[3,C],[4,A]]
Here user_id = 1 has two followers A,B and user_id = 2 has two followers B,D
How to do this?
As pointed out by Sergio, you could use group_by with the array that you get after pluck, for example:
arr = [[1,'A'],[2,'B'],[3,'C'],[4,'A'],[1,'B'],[2,'D']]
a.group_by(&:first).map { |k, v| [k, v.map(&:last)] }
#=> [[1, ["A", "B"]], [2, ["B", "D"]], [3, ["C"]], [4, ["A"]]]
Another approach
a = [[1,'A'],[2,'B'],[3,'C'],[4,'A'],[1,'B'][2,'D']]
h = Hash.new { |h, k| h[k] = [] }
a.inject(h) { |sum, t| sum[t[0]] << t[1]; sum}.to_a

Query has_many relationship, contains this and also this

# engine.rb
has_many :pistons
#piston.rb
belongs_to :engine
Piston has a column, piston_count and, of course, engine_id
My database has the following 7 records
Engine.all
#=> [#<Engine id: 1>, #<Engine id: 2>, #<Engine id: 3>]
Piston.all
#=> [#<Piston id: 1, engine_id: 1, piston_count: 1>, #<Piston id: 2, engine_id: 1, piston_count: 2>, #<Piston id: 2, engine_id: 2, piston_count: 1>, #<Piston id: 2, engine_id: 3, piston_count: 2>]
I want to write a query that says, return the Engine containing Pistons with a piston_count of 1 and also contains a piston_count of 2
I've tried...
engines = Engine.joins(:pistons).merge(Piston.where(piston_count: 1))
#=> [#, #]
engines.joins(:pistons).merge(Piston.where(piston_count:2))
#=> []
It returns an empty array because active record turns that into one AND clause. However, if I do an OR statement, it will return too many records. Any thoughts?
Figured it out. This takes the intersect of both Active Record Queries.
engine_ids = Engine.joins(:pistons).merge(Piston.where(piston_count: 1)).pluck(:id) & Engine.joins(:pistons).merge(Piston.where(piston_count: 2)).pluck(:id)
Then go back and retrieve all the intersects.
Engine.where(id: engine_ids)