ホロライブ課金者ランキング を作ってみたわけだが。
チャンネル情報を全部アイコン画像に載せる方法は良い感じ。スマホ特化のHatena Blogテンプレートでも見やすい。
こんなアイコンのみのランキング作っても誰が理解できるんだろう?は、不安材料では有ったが、有識者に「このランキングに知ってるアカウント有る?」と聞いてみたところ、「7割しってる」「たつのことふぁんでっどと野うさぎはたぶん全員わかる」との回答を頂きましたので、分かる人には分かるランキンにはなっている模様。
じゃ、動画ランキングもチャンネルランキングも基本アイコンでやる方向で良いかもしれん。
処理系はSQLメインでやる予定だったが、同一人物判定にc#依存箇所を作ってしまったので、もういくら使っても同じだろうと思いc#を多用している。SQLならメンドイから避けたい処理もc#なら舞える。
以下は前回からの変更点のみ。
前回からの変更点
チャンネルテーブルにサムネイルURL情報を追加。
これは、YouTube.v3.Data叩けば簡単に取れる。
public DtoChannel GetChannelInfo(string channelId) { List<string> requestTypeList = new List<string>(); requestTypeList.Add("id"); requestTypeList.Add("snippet"); //requestTypeList.Add("topicDetails"); var searchListRequest = _youtubeService.Channels.List(requestTypeList); searchListRequest.Id = channelId; var channnelList = searchListRequest.Execute(); if(channnelList.Items.Count == 0) { return null; } else { var channel = channnelList.Items.First(); DtoChannel c = new DtoChannel(); c.Id = channelId; c.PublishedAt = DateTime.Parse(channel.Snippet.PublishedAt); c.Thumbnail0 = LiveCommentCompleted.GetNotNullString(channel.Snippet.Thumbnails?.Default__?.Url); c.Thumbnail1 = LiveCommentCompleted.GetNotNullString(channel.Snippet.Thumbnails?.Medium?.Url); c.Thumbnail2 = LiveCommentCompleted.GetNotNullString(channel.Snippet.Thumbnails?.High?.Url); return c; } }
順位の取得を同一人物判定と同じタイミングでやるように変更。
同一金額を同一順位にしたかった。
public class DtoPaidUser { #region 定数 /// <summary> /// グルーピングKeyになるための最小文字数 /// </summary> private const int KEY_NAME_MIN_COUNT = 3; /// <summary> /// グルーピングKeyになるための最小金額 /// </summary> private const decimal KEY_AMOUNT_MIN = 10000; /// <summary> /// グルーピングValueになるための最小金額 /// </summary> private const decimal VALUE_AMOUNT_MIN = 10000; /// <summary> /// この金額を超えるアイテムが無い限りグループとしない /// </summary> private const decimal GROUPING_AMOUNT_MIN = 45000; #endregion #region プロパティ public long Rank { get; set; } public decimal Amount { get; set; } public string Name { get; set; } public string NameEdited { get; set; } = string.Empty; public string Id { get; set; } public string Url0 { get; set; } public string Url1 { get; set; } /// <summary> /// サブアカウントがない場合は空、ある場合は自分も含めた要素リスト /// </summary> public List<DtoPaidUser> InnerUserList { get; set; } = new List<DtoPaidUser>(); #endregion /// <summary> /// 同一人物判定用文字列を設定する /// </summary> public void SetNameEdited() { NameEdited = Name.Replace(" ", string.Empty); } public string GetSubAccountMarker() { if(InnerUserList.Count != 0) { return "<br /><span style=\"font-size: 80%\">+ SUB</span>"; } else { return string.Empty; } } /// <summary> /// 同一人物判定 /// </summary> /// <param name="dtoPaidUserList">同一人物判定されていないリスト</param> /// <remarks>同一人物が集約されたリスト</remarks> public static List<DtoPaidUser> GetPaidUserListWithInnerUserList(List<DtoPaidUser> dtoPaidUserList) { foreach (var item in dtoPaidUserList) { item.SetNameEdited(); } //自分と同一だと思われるアイテムをValueに詰める var userList = new Dictionary<DtoPaidUser, List<DtoPaidUser>>(); foreach (var user in dtoPaidUserList) { //結合Keyとなるユーザーの選別 if (user.NameEdited.Length >= KEY_NAME_MIN_COUNT && user.Amount >= KEY_AMOUNT_MIN) { ///結合対象となるユーザーの選別 ///( /// ・名前を含んでいる。 /// かつ /// ・IDが自分では無い。 /// かつ /// ・特定金額以上 /// )または /// ( /// ・IDが自分と同じ /// かつ /// (・名前が違う /// または /// ・サムネイル0が違う /// または /// ・サムネイル1が違う /// ) /// ) var q = from innerUser in dtoPaidUserList where ( innerUser.NameEdited.Contains(user.NameEdited) && !user.Id.Equals(innerUser.Id) && innerUser.Amount >= VALUE_AMOUNT_MIN ) || ( user.Id.Equals(innerUser.Id) && ( !user.Name.Equals(innerUser.Name) || !user.Url0.Equals(innerUser.Url0) || !user.Url1.Equals(innerUser.Url1) ) ) select innerUser; var preInnerList = q.ToList(); if(preInnerList.Count != 0) { decimal maxAmount = preInnerList.Max(x => x.Amount); //グループ内に一定金額以上のアイテムが無ければ、同一とはみなさない。 if (maxAmount > GROUPING_AMOUNT_MIN || user.Amount > GROUPING_AMOUNT_MIN) { userList.Add(user, preInnerList); } else { userList.Add(user, new List<DtoPaidUser>()); } }else { userList.Add(user, new List<DtoPaidUser>()); } } else { userList.Add(user, new List<DtoPaidUser>()); } } //結合対象数が多い順でソート var groupedUserDic = userList.OrderByDescending(pair => pair.Value.Count).ToList(); //Valueに詰めた同一アイテムをKeyから削除 int counter = 0; while (true) { if (groupedUserDic.Count <= counter) { break; } var pair = groupedUserDic.ElementAt(counter); for (int i = 0; i < pair.Value.Count; i++) { //内部要素になったアイテムをKeyから削除する groupedUserDic.Remove( (from item in groupedUserDic where pair.Value[i].Id.Equals(item.Key.Id) select item).First()); } counter++; } var groupedUserList = new List<DtoPaidUser>(); //同一人物判定されたオブジェクトの集計 foreach (var keyValue in groupedUserDic) { if(keyValue.Value.Count() != 0) { //集約要素リストの作成 List<DtoPaidUser> innerList = new List<DtoPaidUser>(); innerList.Add(keyValue.Key); innerList.AddRange(keyValue.Value); //集約 DtoPaidUser dpu = new DtoPaidUser(); dpu.Amount = innerList.Sum(x => x.Amount); dpu.Name = keyValue.Key.Name; dpu.Id = keyValue.Key.Id; dpu.Url0 = keyValue.Key.Url0; dpu.Url1 = keyValue.Key.Url1; dpu.InnerUserList = innerList; groupedUserList.Add(dpu); } else { groupedUserList.Add(keyValue.Key); } } return GetRankedUserList(groupedUserList); } /// <summary> /// 金額ごとにランキング順位を設定したリストを取得する /// </summary> /// <param name="userList"></param> /// <returns></returns> public static List<DtoPaidUser> GetRankedUserList(List<DtoPaidUser> userList) { var orderedList = userList.OrderByDescending(x => x.Amount); //順位の設定 int rankCounter = 0; int sameItemCounter = 0; decimal rankAmountValue = -1; foreach (var item in orderedList) { //同一金額の場合同一の順位を設定すための条件 if (rankAmountValue.CompareTo(item.Amount) != 0) { rankCounter += sameItemCounter + 1; sameItemCounter = 0; rankAmountValue = item.Amount; } else { sameItemCounter++; } item.Rank = rankCounter; } return orderedList.ToList(); } }
海外通貨の種類が全然足りなかったので、手動で追加。
rateの自動生成はどうにでもなりそうだが、prifixからisoの自動生成は難易度が高い。
このレートテーブルが、ホロライブ課金者ランキング を作るときに使ったレート
prifix | iso | rate | memo |
---|---|---|---|
ARS | ARS | 1.48 | アルゼンチン・ペソ |
A$ | AUD | 75.54 | オーストラリア・ドル |
BGN | BGN | 63.24 | ブルガリア・レフ |
R$ | BRL | 20.13 | ブラジル・レアル |
CA$ | CAD | 79.45 | カナダ・ドル |
CHF | CHF | 115.27 | スイス・フラン |
CLP | CLP | 0.14 | チリ・ペソ |
CZK | CZK | 4.71 | チェコ・コルナ |
DKK | DKK | 16.63 | デンマーク・クローネ |
€ | EUR | 122.8 | ユーロ |
£ | GBP | 136.17 | UK・ポンド |
GTQ | GTQ | 13.87 | グアテマラ・ケツァル |
HK$ | HKD | 13.84 | 香港・ドル |
HRK | HRK | 16.46 | クロアチア・クーナ |
HUF | HUF | 0.36 | ハンガリー・フォリント |
₹ | INR | 1.43 | インド・ルピー |
¥ | JPY | 1 | 日本・円 |
₩ | KRW | 0.09 | 韓国・ウォン |
MX$ | MXN | 4.78 | メキシコ・ペソ |
NIO | NIO | 3.07 | ニカラグア・コルドバ |
NOK | NOK | 11.59 | ノルウェー・クローネ |
NZ$ | NZD | 70.85 | ニュージーランド・ドル |
PEN | PEN | 30.46 | ペルー・ソル |
PHP | PHP | 2.17 | フィリピン・ペソ |
PLN | PLN | 27.6 | ポーランド・ズウォティ |
PYG | PYG | 0.015 | パラグアイ・グアラニー |
RON | RON | 25.37 | ルーマニア・レウ |
RUB | RUB | 1.49 | ロシア・ルーブル |
SEK | SEK | 12.05 | スウェーデン・クローナ |
SGD | SGD | 77.18 | シンガポール・ドル |
NT$ | TWD | 3.64 | ニュー台湾・ドル |
$ | USD | 107.3 | US・ドル |
UYU | UYU | 2.5 | ウルグアイ・ペソ |
ZAR | ZAR | 6.41 | 南アフリカ共和国・ランド |