こんにちは、がきけです!
DartのMapについて、自作のクラスを用いた要素の検索でつまずいたので、
備忘録としてまとめます。
Mapに自作クラスの形式で要素が入っている場合の検索方法
やりたかったこと
今回つまずいたのは、以下に記載したコードのように、
Mapの要素の形式がStringなどの既存の型ではなく、自作したクラスで、
クラスの中にuserNameやcreatedDateなど複数の要素が入っている場合に、
Mapのvalueに該当するユーザーが登録されているかどうかを確認する方法です。
void main() {
// 登録しているユーザーのデータ(自作のクラス'User'の形式で要素を登録)
Map<String,User> users = {
'1': User(userName: 'user1', createdDate: DateTime(2022, 11, 1)),
'2': User(userName: 'user2', createdDate: DateTime(2022, 11, 2)),
};
bool checkUser(String checkUserName, DateTime checkDate){
// ここに該当するユーザーが登録されているかどうかを判定するコードを書く
}
print(checkUser('user1',DateTime(2022,11,1))); // trueで返してほしい
print(checkUser('user3',DateTime(2022,11,3))); // falseで返してほしい
}
class User{
final String userName;
final DateTime createdDate;
User({
required this.userName,
required this.createdDate,
});
}
containsValueは失敗
「あるかどうか」がほしかったので、bool型で返してほしいなーと思い、
最初に試してみたのがcontainsValueメソッド。
Mapのvalueの中に、該当する要素があるかどうかを確認することができるメソッドです。
こんな感じでcheckUserの中身を書きました。
void main() {
// 登録しているユーザーのデータ
Map<String,User> users = {
'1': User(userName: 'user1', createdDate: DateTime(2022, 11, 1)),
'2': User(userName: 'user2', createdDate: DateTime(2022, 11, 2)),
};
bool checkUser(String checkUserName, DateTime checkDate){
bool isUser;
// ここ
users.containsValue(User(userName: checkUserName, createdDate: checkDate)) ? isUser = true : isUser = false;
return isUser;
}
print(checkUser('user1',DateTime(2022,11,1))); // trueで返してほしい
print(checkUser('user3',DateTime(2022,11,3))); // falseで返してほしい
}
が、しかしこれが失敗。
checkUser('user1',DateTime(2022,11,1))をtrueで返してほしかったのですが、falseで返ってきてしまいました。
どうやらcontainsValueはStringやDateTime単体であればうまくいくのですが、
今回のUserのように、複数の要素があるvalueについては検索ができないようなのです。
void main() {
// これだとうまくいく
Map<String,DateTime> users = {
'user1': DateTime(2022, 11, 1),
'user2': DateTime(2022, 11, 2),
};
bool checkUser(String checkUserName, DateTime checkDate){
bool isUser;
// DateTime型の要素だけで検索
users.containsValue(checkDate) ? isUser = true : isUser = false;
return isUser;
}
print(checkUser('user1',DateTime(2022,11,1))); // trueで返ってきました
print(checkUser('user3',DateTime(2022,11,3))); // falseで返ってきました
}
where.isNotEmptyで成功
さてどうしようということで、bool型で返ってこなくても、
「該当するものがあればそれを返してー」と言って1つ以上返ってきたらtrue、
返ってこなかったらfalseにするようにすればいいかなーと思い、whereで要素を検索することにしました。
map.entries.where((element) => 条件式)で、条件式に合う要素をすべて取得することができます。
すると、map.entries.where((element) => 条件式)に.isNotEmptyをつけることで、
条件式に合う要素があるかどうかをbool型で取得することができることを発見。
実行してみたところ、正常に動作し、目的を達成することができました。
void main() {
Map<String,User> users = {
'1': User(userName: 'user1', createdDate: DateTime(2022, 11, 1)),
'2': User(userName: 'user2', createdDate: DateTime(2022, 11, 2)),
};
bool checkUser(String checkUserName, DateTime checkDate){
bool isUser;
// Mapに登録されているuserNameと引数で渡されたcheckUserNameが一致して、
// かつMapに登録されているcreatedDateと引数で渡されたcheckDateが一致した場合のみtrueを返すようにしました
users.entries.where((element) => element.value.userName == checkUserName && element.value.createdDate == checkDate).isNotEmpty ? isUser = true : isUser = false;
return isUser;
}
print(checkUser('user1',DateTime(2022,11,1))); // trueで返ってきました
print(checkUser('user3',DateTime(2022,11,3))); // falseで返ってきました
}
今後の課題
「containsValueでは自作クラスの形式で登録された要素を検索できない」というのは、
ちゃんと公式ドキュメントに書いてあったわけではなくて、
自分で動かしてみて、そうなのかな? と思ったことです。
なので、より詳しいことをご存知の方がいらっしゃいましたら、コメントでご教示いただければ幸いです。
また、「もっといい書き方あるよー」や「こんなんどう?」などありましたら、
同じくコメントいただければ嬉しいです。よろしくお願いします。