LoginSignup
0
2

More than 5 years have passed since last update.

同一インスタンスでない List を property でまとめて数える

Posted at

概要

例えば、こういうスキルの class があったとして、

Skill.cs
public class Skill
{
    public int Code { get; private set; }
    public int Level { get; private set; }

    public Skill(int code, int level)
    {
        Code = code;
        Level = level;
    }
}

同じスキル・レベルでも別インスタンスである可能性のある List<Skill> から、同じスキル・レベルをまとめて数える IDictionary<Skill, int> SkillCountDictionary(List<Skill>) を実装します。

テスト

SkillTest.cs

public class SkillTest
{

    [Test]
    public void SkillCountDictionary()
    {
        CollectionAssert.AreEquivalent(
            Skill.SkillCountDictionary(Skills()),
            Expect()
        );
    }

    private List<Skill> Skills()
    {
        var skills = new List<Skill>();
        skills.Add(new Skill(1, 1));
        skills.Add(new Skill(1, 1));
        skills.Add(new Skill(2, 1));
        skills.Add(new Skill(2, 2));
        skills.Add(new Skill(2, 2));
        skills.Add(new Skill(2, 2));
        return skills;
    }

    private Dictionary<Skill, int> Expect()
    {
        return new Dictionary<Skill, int>
        {
            {new Skill(1, 1), 2},
            {new Skill(2, 1), 1},
            {new Skill(2, 2), 3}
        };
    }
}

foreach で Dictionary に入れていく実装

Skill.cs

    public static IDictionary<Skill, int> SkillCountDictionary(List<Skill> skills)
    {
        var testCountDictionary = new Dictionary<Skill, int>();
        foreach (var skill in skills)
        {
            // csharp> dict[0]++; => System.Collections.Generic.KeyNotFoundException
            if (!testCountDictionary.ContainsKey(skill))
            {

                testCountDictionary[skill] = 0;
            }
            testCountDictionary[skill]++;
        }
        return testCountDictionary;
    }

当然通りません。
インスタンスが違うので、それぞれ別の Key になってしまいます。

IEquatable を実装する

Skill.cs
public class Skill : IEquatable<Skill>
{

    public override int GetHashCode()
    {
        // Tuple の実装があればこっちの方がイケてる気がする
        // return Tuple.Create(Code, Level).GetHashCode();
        return Code.GetHashCode() ^ Level.GetHashCode();
    }

    bool IEquatable<Skill>.Equals(Skill other)
    {
        if (other == null)
        {
            return false;
        }

        return Code == other.Code && Level == other.Level;
    }

通りました。けど、長い。

LINQ GroupBy を使う実装

Skill.cs
    public static IDictionary<Skill, int> SkillCountDictinary(List<Skill> skills)
    {
        return skills.GroupBy(s => new { s.Code, s.Level })
            .ToDictionary(g => g.First(), g => g.Count());
    }

1行で書けました。

まとめ

  • GroupBy が便利でした。
  • IEquatable<T> は覚えておくといいかもしれない。
    • もうちょっと一般的な場面でも役に立ちそう。
    • Tuple が欲しくなる。
0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2