Rails - Dealing with array of objects - ruby-on-rails

I have an array of students objects (Student.all) and each object has a name and a class_id.
I want to find students with same class_id and concat their names into the same object, the others remain the same.
I mean, turn this:
[
{
id: 1,
name: 'Joe',
class_id: 55
},
{
id: 2,
name: 'Bill',
class_id: 55
},
{
id: 3,
name: 'Moe',
class_id: 70
},
{
id: 4,
name: 'Larry',
class_id: 80
},
{
id: 5,
name: 'Phill',
class_id: 80
}
]
Into this:
[
{
id: 1,
name: 'Joe/Bill',
class_id: 55
},
{
id: 3,
name: 'Moe',
class_id: 70
},
{
id: 4,
name: 'Larry/Phill',
class_id: 80
}
]

Assuming you're only after creating the array (as opposed to updating the DB) you can do something like this:
arr.group_by{|x| x[:class_id]}
.values.map{|x| x.reduce{|m,v| m[:name] = "#{m[:name]}/#{v[:name]}";m}}
If you care about the original array you can make a small change:
arr.group_by{|x| x[:class_id]}
.values.map{|x| x[1..-1].reduce(x.first){|m,v| m[:name] = "#{m[:name]}/#{v[:name]}";m}}
The result:
[
{:id=>1, :name=>"Joe/Joe/Bill", :class_id=>55},
{:id=>3, :name=>"Moe", :class_id=>70},
{:id=>4, :name=>"Larry/Larry/Phill", :class_id=>80}
]

My solution is to use Enumerable#group_by.
students = [...] # the source array
students.group_by(&:class_id)
# => {
55 => [
{
:id => 1,
:name => "Joe",
:class_id => 55
},
{
:id => 2,
:name => "Bill",
:class_id => 55
}
],
70 => [
{
:id => 3,
:name => "Moe",
:class_id => 70
}
],
80 => [
{
:id => 4,
:name => "Larry",
:class_id => 80
},
{
:id => 5,
:name => "Phil",
:class_id => 80
}
]
}
# Code in reduce block may need to be changed to fit your demand
students.group_by(&:class_id).reduce([]) do |ret, (k,v)|
st = v.first
st.name = v.map(&:name).join('/')
ret << st
end
# => target

Related

What does X-Apple-Store-Front means in apple http headers?

I need get hot search keywords using apple API https://search.itunes.apple.com/WebObjects/MZSearchHints.woa/wa/trends, and set values {"X-Apple-Store-Front": "143465-19,30"} in http
headers, if i set "X-Apple-Store-Front" to "143465-19,30", the result is
{
"header": {
"label": "热门搜索"
},
"trendingSearches": [
{
"label": "蜜芽",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=Software&src=trending&term=%E8%9C%9C%E8%8A%BD"
},
{
"label": "qq",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=Software&src=trending&term=qq"
},
{
"label": "腾讯视频",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=Software&src=trending&term=%E8%85%BE%E8%AE%AF%E8%A7%86%E9%A2%91"
},
{
"label": "微信",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=Software&src=trending&term=%E5%BE%AE%E4%BF%A1"
},
{
"label": "爱奇艺",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=Software&src=trending&term=%E7%88%B1%E5%A5%87%E8%89%BA"
},
{
"label": "淘宝",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=Software&src=trending&term=%E6%B7%98%E5%AE%9D"
},
{
"label": "百度",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=Software&src=trending&term=%E7%99%BE%E5%BA%A6"
},
{
"label": "qq音乐",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=Software&src=trending&term=qq%E9%9F%B3%E4%B9%90"
},
{
"label": "微博",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=Software&src=trending&term=%E5%BE%AE%E5%8D%9A"
},
{
"label": "百度网盘",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=Software&src=trending&term=%E7%99%BE%E5%BA%A6%E7%BD%91%E7%9B%98"
}
]
}
if i set "X-Apple-Store-Front" to "143465-19,29", the result will change.
{
"header": {
"label": "热门搜索"
},
"trendingSearches": [
{
"label": "铃声",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=MusicPlayer&src=trending&term=%E9%93%83%E5%A3%B0"
},
{
"label": "李荣浩",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=MusicPlayer&src=trending&term=%E6%9D%8E%E8%8D%A3%E6%B5%A9"
},
{
"label": "泰勒·斯威夫特",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=MusicPlayer&src=trending&term=%E6%B3%B0%E5%8B%92%C2%B7%E6%96%AF%E5%A8%81%E5%A4%AB%E7%89%B9"
},
{
"label": "像我这样的人",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=MusicPlayer&src=trending&term=%E5%83%8F%E6%88%91%E8%BF%99%E6%A0%B7%E7%9A%84%E4%BA%BA"
},
{
"label": "eminem",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=MusicPlayer&src=trending&term=eminem"
},
{
"label": "杨宗纬",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=MusicPlayer&src=trending&term=%E6%9D%A8%E5%AE%97%E7%BA%AC"
},
{
"label": "张碧晨",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=MusicPlayer&src=trending&term=%E5%BC%A0%E7%A2%A7%E6%99%A8"
},
{
"label": "刘若英",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=MusicPlayer&src=trending&term=%E5%88%98%E8%8B%A5%E8%8B%B1"
},
{
"label": "lady gaga",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=MusicPlayer&src=trending&term=lady%20gaga"
},
{
"label": "赵雷",
"url": "https://search.itunes.apple.com/WebObjects/MZStore.woa/wa/search?clientApplication=MusicPlayer&src=trending&term=%E8%B5%B5%E9%9B%B7"
}
]
}
, pls tell me what does mean and how to use X-Apple-Store-Front, and the X-Apple-Store-Front mapping?
THis X-Apple-Store-Front is a country code. For more info:
https://affiliate.itunes.apple.com/resources/documentation/linking-to-the-itunes-music-store/
143465-19,29 means CN store, zh-cn language, P84 platform
It's ${storefrontID}-${languageVariationID},${platformID}. More info here. Language variations are explained a bit here. Basically, if you leave off the -${languageVariationID}, you'll get a decent default for storefront's country.
If you want a non-default language, the language-variations are here:
{
'pt-br': 15,
'it-it': 7,
'hi-in': 50,
'ca-es': 42,
'es-es': 8,
'zh-tw': 18,
'th-th': 35,
'da-dk': 11,
'fi-fi': 12,
'en-gb': 2,
'nl-nl': 10,
'pl-pl': 20,
'ro-ro': 39,
'zh-cn': 19,
'no-no': 14,
'pt-pt': 24,
'de-ch': 57,
'uk-ua': 29,
'en-us': 1,
'vi-vi': 43,
'sv-se': 17,
'en-ca': 6,
'fr-fr': 3,
'sk-sk': 40,
'zh-hk': 45,
'hr-hr': 41,
'en-au': 27,
'ko-kr': 13,
'tr-tr': 25,
'el-gr': 23,
'ms-my': 38,
'id-id': 37,
'cs-cz': 22,
'hu-hu': 21,
'fr-ca': 5,
'es-mx': 28,
'de-de': 4,
'ru-ru': 16,
'ja-jp': 9
}
The platform-ids available are these:
{
K7: 20,
P7: 21,
K71: 23,
P71: 24,
K8: 25,
P8: 26,
P84: 29,
K84: 30,
Android: 31,
Watch: 35,
MacPodcasts1: 38
}
And the store-ids are here:
{
DZ: 143563,
AO: 143564,
AI: 143538,
AR: 143505,
AM: 143524,
AU: 143460,
AT: 143445,
AZ: 143568,
BH: 143559,
BB: 143541,
BY: 143565,
BE: 143446,
BZ: 143555,
BM: 143542,
BO: 143556,
BW: 143525,
BR: 143503,
VG: 143543,
BN: 143560,
BG: 143526,
CA: 143455,
KY: 143544,
CL: 143483,
CN: 143465,
CO: 143501,
CR: 143495,
HR: 143494,
CY: 143557,
CZ: 143489,
DK: 143458,
DM: 143545,
EC: 143509,
EG: 143516,
SV: 143506,
EE: 143518,
FI: 143447,
FR: 143442,
DE: 143443,
GH: 143573,
GR: 143448,
GD: 143546,
GT: 143504,
GY: 143553,
HN: 143510,
HK: 143463,
HU: 143482,
IS: 143558,
IN: 143467,
ID: 143476,
IE: 143449,
IL: 143491,
IT: 143450,
JM: 143511,
JP: 143462,
JO: 143528,
KE: 143529,
KW: 143493,
LV: 143519,
LB: 143497,
LT: 143520,
LU: 143451,
MO: 143515,
MK: 143530,
MG: 143531,
MY: 143473,
ML: 143532,
MT: 143521,
MU: 143533,
MX: 143468,
MS: 143547,
NP: 143484,
NL: 143452,
NZ: 143461,
NI: 143512,
NE: 143534,
NG: 143561,
NO: 143457,
OM: 143562,
PK: 143477,
PA: 143485,
PY: 143513,
PE: 143507,
PH: 143474,
PL: 143478,
PT: 143453,
QA: 143498,
RO: 143487,
RU: 143469,
SA: 143479,
SN: 143535,
SG: 143464,
SK: 143496,
SI: 143499,
ZA: 143472,
ES: 143454,
LK: 143486,
SR: 143554,
SE: 143456,
CH: 143459,
TW: 143470,
TZ: 143572,
TH: 143475,
TN: 143536,
TR: 143480,
UG: 143537,
UA: 143492,
AE: 143481,
US: 143441,
UY: 143514,
UZ: 143566,
VE: 143502,
VN: 143471,
YE: 143571
}
A (small) update on this one:
X-Apple-Store-Front work with ID's in this order:
<country>-<language*>,<platform>
Where language is optional. Default is EN-US.
Example: 143452-10,9 (-Dutch,Apps)
Here's an updated list of country codes:
'AE' => '143481',
'AF' => '143610',
'AG' => '143540',
'AI' => '143538',
'AL' => '143575',
'AM' => '143524',
'AO' => '143564',
'AR' => '143505',
'AT' => '143445',
'AU' => '143460',
'AZ' => '143568',
'BA' => '143612',
'BB' => '143541',
'BD' => '143490',
'BE' => '143446',
'BF' => '143578',
'BG' => '143526',
'BH' => '143559',
'BJ' => '143576',
'BM' => '143542',
'BN' => '143560',
'BO' => '143556',
'BR' => '143503',
'BS' => '143539',
'BT' => '143577',
'BW' => '143525',
'BY' => '143565',
'BZ' => '143555',
'CA' => '143455',
'CD' => '143613',
'CG' => '143582',
'CH' => '143459',
'CI' => '143527',
'CL' => '143483',
'CM' => '143574',
'CN' => '143465',
'CO' => '143501',
'CR' => '143495',
'CV' => '143580',
'CY' => '143557',
'CZ' => '143489',
'DE' => '143443',
'DK' => '143458',
'DM' => '143545',
'DO' => '143508',
'DZ' => '143563',
'EC' => '143509',
'EE' => '143518',
'EG' => '143516',
'ES' => '143454',
'FI' => '143447',
'FJ' => '143583',
'FM' => '143591',
'FR' => '143442',
'GA' => '143614',
'GB' => '143444',
'GD' => '143546',
'GF' => '143615',
'GH' => '143573',
'GM' => '143584',
'GR' => '143448',
'GT' => '143504',
'GW' => '143585',
'GY' => '143553',
'HK' => '143463',
'HN' => '143510',
'HR' => '143494',
'HU' => '143482',
'ID' => '143476',
'IE' => '143449',
'IL' => '143491',
'IN' => '143467',
'IQ' => '143617',
'IS' => '143558',
'IT' => '143450',
'JM' => '143511',
'JO' => '143528',
'JP' => '143462',
'KE' => '143529',
'KG' => '143586',
'KH' => '143579',
'KN' => '143548',
'KP' => '143466',
'KR' => '143466',
'KW' => '143493',
'KY' => '143544',
'KZ' => '143517',
'LA' => '143587',
'LB' => '143497',
'LC' => '143549',
'LI' => '143522',
'LK' => '143486',
'LR' => '143588',
'LT' => '143520',
'LU' => '143451',
'LV' => '143519',
'LY' => '143567',
'MA' => '143620',
'MD' => '143523',
'ME' => '143619',
'MG' => '143531',
'MK' => '143530',
'ML' => '143532',
'MM' => '143570',
'MN' => '143592',
'MO' => '143515',
'MR' => '143590',
'MS' => '143547',
'MT' => '143521',
'MU' => '143533',
'MV' => '143488',
'MW' => '143589',
'MX' => '143468',
'MY' => '143473',
'MZ' => '143593',
'NA' => '143594',
'NE' => '143534',
'NG' => '143561',
'NI' => '143512',
'NL' => '143452',
'NO' => '143457',
'NP' => '143484',
'NR' => '143606',
'NZ' => '143461',
'OM' => '143562',
'PA' => '143485',
'PE' => '143507',
'PG' => '143597',
'PH' => '143474',
'PK' => '143477',
'PL' => '143478',
'PT' => '143453',
'PW' => '143595',
'PY' => '143513',
'QA' => '143498',
'RO' => '143487',
'RS' => '143500',
'RU' => '143469',
'RW' => '143621',
'SA' => '143479',
'SB' => '143601',
'SC' => '143599',
'SE' => '143456',
'SG' => '143464',
'SI' => '143499',
'SK' => '143496',
'SL' => '143600',
'SN' => '143535',
'SR' => '143554',
'ST' => '143598',
'SV' => '143506',
'SZ' => '143602',
'TC' => '143552',
'TD' => '143581',
'TH' => '143475',
'TJ' => '143603',
'TM' => '143604',
'TN' => '143536',
'TO' => '143608',
'TR' => '143480',
'TT' => '143551',
'TW' => '143470',
'TZ' => '143572',
'UA' => '143492',
'UG' => '143537',
'US' => '143441',
'UY' => '143514',
'UZ' => '143566',
'VC' => '143550',
'VE' => '143502',
'VG' => '143543',
'VN' => '143471',
'VU' => '143609',
'XK' => '143624',
'YE' => '143571',
'ZA' => '143472',
'ZM' => '143622',
'ZW' => '143605'
I haven't found a 'platform' list (yet).

MongoDB count by date AND to date

I have a User table with "created" date field. Now I want to know how many new users were created on each day last week, as well as the total users by the end of each day (since the beginning of time).
For example:
{
{
day: 27,
new_users: 5,
total_users: 100
}, {
day: 28,
new_users: 7,
total_users: 107
}, {
day: 29,
new_users: 2,
total_users: 109
}
}
I already got the new_users part by using simple grouping and summing. Code below is mongoid/Ruby.
results = User.collection.aggregate(
[{
"$match" => {
created: { "$gte" => 1.week.ago.beginning_of_day, "$lte" => Time.now }
}
},
{
"$group" => {
_id: {
year_joined: { "$year" => "$created" },
month_joined: { "$month" => "$created" },
day_joined: { "$dayOfMonth" => "$created" }
},
count: { "$sum" => 1 }
}
},
{
"$sort" => {"_id.year_joined" => 1, "_id.month_joined" => 1, "_id.day_joined" => 1}
}]
)
How do I also get the total_users in the results?
You're getting close with your attempt. One thing is that since you're starting out by filtering down to own the last week, you don't need to care about the year or month.
Note: I don't know ruby, so I wrote it as I would in the mongo shell.
{
"$match" => {
created: { "$gte" => 1.week.ago.beginning_of_day, "$lte" => Time.now }
}
},
{
$group: {
_id: {"$dayOfMonth": "$created" },
day: {"$dayOfMonth": "$created" },
newUsers: { "$sum": 1}
}
},
{
"$sort" => {"_id.year_joined" => 1, "_id.month_joined" => 1, "_id.day_joined" => 1}
}
That will get you the day and newUsers, but it wont get you the total. I don't actually that is possible in a single query. One alternative might be to give each user a unique number and just take the max of the numbers for that day.

Attempted to handle event `loadedData` on while in state rootState.loaded.updated.uncommitted. Called with undefined

I am using ember-data to fetch json response from server side, my embeer model looks like.
App.Field = DS.Model.extend
component: DS.attr 'string'
fieldinfos: DS.hasMany('App.Fieldinfo')
App.Field.reopenClass
forInstitution: (institution)->
App.Field.find { institution_id: institution.id }
and my json response looks like
{
:login_fields => [
[0] {
:component => "ns4:FieldInfoComponent",
:id => 1,
:fieldinfo_ids => [
[0] 1
]
},
[1] {
:component => "ns4:FieldInfoComponent",
:id => 2,
:fieldinfo_ids => [
[0] 2
]
},
[2] {
:component => "ns4:FieldInfoComponent",
:id => 3,
:fieldinfo_ids => [
[0] 3
]
}
],
:fieldinfos => [
[0] {
:field_info_obj => "FieldInfoSingle",
:login_field_id => 1,
:id => 1,
:element_ids => [
[0] 1
]
},
[1] {
:field_info_obj => "FieldInfoSingle",
:login_field_id => 2,
:id => 2,
:element_ids => [
[0] 2
]
},
[2] {
:field_info_obj => "FieldInfoSingle",
:login_field_id => 3,
:id => 3,
:element_ids => [
[0] 3
]
}
]
}
it works perfectly when I call once
App.Field.find { institution_id: 1 }
but in between if I again call for different institution
App.Field.find { institution_id: 2 }
Following is the code that fetch loginfields whenever institution changes
loginFields: (->
return [] unless #get('institution')?
App.LoginField.forInstitution(#get 'institution')
).property 'institution'
It gives me Attempted to handle event loadedData on while in state rootState.loaded.updated.uncommitted. Called with undefined error
what I am doing wrong over here and what I need to do to resolve this error. help!

Preparing data for graph

I would like some help in order to prepare my data gathered from the database for charting.
I have a Browser model where I store all the data.
From each model I want to select the name attribute, I will put a color according an integer attribute in the model (for example if integer is 1, color => "#4572A7") and the y attribute from the model.
Can someone provide an example of the most efficient way to achieve this data format?
Final format of the data:
[
{
:name=> 'Firefox',
:y=> 1,
:color => "#4572A7"
},
{
:name=> 'IE',
:y=> 1,
:color => "#AA4643"
},
{
:name=> 'Chrome',
:y=> 1,
:color => "#89A54E"
},
{
:name=> 'Safari',
:y=> 1,
:color => "#80699B"
},
{
:name=> 'Opera',
:y=> 1,
:color => "#3D96AE"
},
{
:name=> 'Others',
:y=> 1,
:color => "#DB843D"
}
]
You could have a method that does the number-to-color translation and have something like
#browsers.to_json(methods: [:the_method_that_translates_numbers_to_colors], only: [:name, :y])
Hope that helps,
NHI

Hash remove all except specific keys

I would like to remove every key from a hash except a given key.
For example:
{
"firstName": "John",
"lastName": "Smith",
"age": 25,
"address":
{
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021"
},
"phoneNumber":
[
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "fax",
"number": "646 555-4567"
}
]
}
I want to remove everything except "firstName" and/or "address".
What about slice?
hash.slice('firstName', 'lastName')
# => { 'firstName' => 'John', 'lastName' => 'Smith' }
Available in Ruby since 2.5
Some other options:
h.select {|k,v| ["age", "address"].include?(k) }
Or you could do this:
class Hash
def select_keys(*args)
select {|k,v| args.include?(k) }
end
end
So you can now just say:
h.select_keys("age", "address")
If you use Rails, please consider ActiveSupport except() method: http://apidock.com/rails/Hash/except
hash = { a: true, b: false, c: nil}
hash.except!(:c) # => { a: true, b: false}
hash # => { a: true, b: false }
Hash#select does what you want:
h = { "a" => 100, "b" => 200, "c" => 300 }
h.select {|k,v| k > "a"} #=> {"b" => 200, "c" => 300}
h.select {|k,v| v < 200} #=> {"a" => 100}
Edit (for comment):
assuming h is your hash above:
h.select {|k,v| k == "age" || k == "address" }
hash = { a: true, b: false, c: nil }
hash.extract!(:c) # => { c: nil }
hash # => { a: true, b: false }
Inspired by Jake Dempsey's answer, this one should be faster for large hashes, as it only peaks explicit keys rather than iterating through the whole hash:
class Hash
def select_keys(*args)
filtered_hash = {}
args.each do |arg|
filtered_hash[arg] = self[arg] if self.has_key?(arg)
end
return filtered_hash
end
end
No Rails needed to get a very concise code:
keys = [ "firstName" , "address" ]
# keys = hash.keys - (hash.keys - keys) # uncomment if needed to preserve hash order
keys.zip(hash.values_at *keys).to_h

Resources