計算機科学のブログ

LINQ and lambdas - Get control of your data - group

Head First C Sharp: A Learner’s Guide to Real-World Programming with C Sharp and .NET (Andrew Stellman(著)、Jennifer Greene(著)、O’Reilly Media)の Chapter 9(LINQ and lambdas - Get control of your data)、p.497(Exercise)の解答を求めてみる。

コード

Program.cs

using System.Runtime.InteropServices.Marshalling;
using JimmyLinq;

namespace JimmyLinq
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var done = false;
            while (!done)
            {
                Console.WriteLine(
                    "\nPress G to group comics by price, R to get reviews, any other key to quit\n");
                switch (Console.ReadKey(true).KeyChar.ToString().ToUpper())
                {
                    case "G":
                        done = GroupComicsByPrice();
                        break;
                    case "R":
                        done = GetReviews();
                        break;
                    default:
                        done = true;
                        break;
                }
            }
        }
        private static bool GroupComicsByPrice()
        {
            var groups = ComicAnalyzer.GroupComicsByPrice(Comic.Catalog, Comic.Prices);
            foreach (var group in groups)
            {
                Console.WriteLine($"{group.Key} comics");
                foreach (var comic in group)
                {
                    Console.WriteLine($"#{comic.Issue} {comic.Name}: {Comic.Prices[comic.Issue]:c}");
                }
            }
            return false;
        }
        private static bool GetReviews()
        {
            var reviews = ComicAnalyzer.GetReviews(Comic.Catalog, Comic.Reviews);
            foreach (var review in reviews)
            {
                Console.WriteLine(review);
            }
            return false;
        }

        private static class ComicAnalyzer
        {
            private static PriceRange CalculatePriceRange(Comic comic)
            {
                if (Comic.Prices[comic.Issue] < 100)
                {
                    return PriceRange.Cheap;
                }
                return PriceRange.Expensive;
            }
            public static IEnumerable<IGrouping<PriceRange, Comic>> GroupComicsByPrice(IEnumerable<Comic> catalog, IReadOnlyDictionary<int, decimal> prices)
            {
                return from comic in catalog
                       orderby prices[comic.Issue]
                       group comic by CalculatePriceRange(comic) into grouped
                       select grouped;

            }

            public static IEnumerable<object> GetReviews(IEnumerable<Comic> catalog, IEnumerable<Review> reviews)
            {
                return from comic in catalog
                       orderby comic.Issue
                       join review in reviews
                       on comic.Issue equals review.Issue
                       select $"{review.Critic} rated #{comic.Issue} '{comic.Name}' {review.Score:0.00}";
            }
        }
    }
}

Comic.cs

namespace JimmyLinq;

public class Comic
{
    public string Name { get; set; } = "";
    public int Issue { get; set; }
    public override string ToString()
    {
        return $"{Name} (Issue #{Issue})";
    }
    public static readonly IEnumerable<Comic> Catalog =
     new List<Comic>{
        new Comic(){Name="Johnny America vs. the Pinko", Issue=6},
        new Comic(){Name="Rock and Roll (limitededition)", Issue=19},
        new Comic(){Name="Woman's Work", Issue=36},
        new Comic(){Name="Hippie Madness (misprinted)", Issue=57},
        new Comic(){Name="Revenge of the New Wave Freak (damaged)", Issue=68},
        new Comic(){Name="Black Monday", Issue=74},
        new Comic(){Name="Tattoo Madness", Issue=83},
        new Comic(){Name="The Death of the Object", Issue=97},
    };

    public static readonly IEnumerable<Review> Reviews = new[]
    {
        new Review() { Issue = 36, Critic = Critics.MuddyCritic, Score = 37.6},
        new Review() { Issue = 74, Critic = Critics.RottenTornadoes, Score =22.8 },
        new Review() { Issue = 74, Critic = Critics.MuddyCritic, Score = 87.2 },
        new Review() { Issue = 83, Critic = Critics.RottenTornadoes, Score = 89.4 },
        new Review() { Issue = 97, Critic = Critics.MuddyCritic, Score = 98.1 },
    };
    public static readonly IReadOnlyDictionary<int, decimal> Prices =
        new Dictionary<int, decimal>{
            {6, 3600M},
            {19,500M},
            {36,650M},
            {57,13525M},
            {68,250M},
            {74,75M},
            {83,25.75M},
            {97,35.25M},
    };
}

Critics.cs

namespace JimmyLinq;

public enum Critics
{
    MuddyCritic,
    RottenTornadoes,
}

PriceRange.cs

namespace JimmyLinq;

public enum PriceRange
{
    Cheap,
    Expensive
}

Review.cs

namespace JimmyLinq;

public class Review
{
    public int Issue { get; set; }
    public Critics Critic { get; set; }
    public double Score { get; set; }
}

入出力結果(Terminal, Zsh)

% dotnet run

Press G to group comics by price, R to get reviews, any other key to quit

Cheap comics
#83 Tattoo Madness: ¥26
#97 The Death of the Object: ¥35
#74 Black Monday: ¥75
Expensive comics
#68 Revenge of the New Wave Freak (damaged): ¥250
#19 Rock and Roll (limitededition): ¥500
#36 Woman's Work: ¥650
#6 Johnny America vs. the Pinko: ¥3,600
#57 Hippie Madness (misprinted): ¥13,525

Press G to group comics by price, R to get reviews, any other key to quit

MuddyCritic rated #36 'Woman's Work' 37.60
RottenTornadoes rated #74 'Black Monday' 22.80
MuddyCritic rated #74 'Black Monday' 87.20
RottenTornadoes rated #83 'Tattoo Madness' 89.40
MuddyCritic rated #97 'The Death of the Object' 98.10

Press G to group comics by price, R to get reviews, any other key to quit

MuddyCritic rated #36 'Woman's Work' 37.60
RottenTornadoes rated #74 'Black Monday' 22.80
MuddyCritic rated #74 'Black Monday' 87.20
RottenTornadoes rated #83 'Tattoo Madness' 89.40
MuddyCritic rated #97 'The Death of the Object' 98.10

Press G to group comics by price, R to get reviews, any other key to quit

Cheap comics
#83 Tattoo Madness: ¥26
#97 The Death of the Object: ¥35
#74 Black Monday: ¥75
Expensive comics
#68 Revenge of the New Wave Freak (damaged): ¥250
#19 Rock and Roll (limitededition): ¥500
#36 Woman's Work: ¥650
#6 Johnny America vs. the Pinko: ¥3,600
#57 Hippie Madness (misprinted): ¥13,525

Press G to group comics by price, R to get reviews, any other key to quit

%