Creating a dictionary with keys representing elements from a list

Viewed 90

I want to create a Dictionary where the Keys are representing elements from a List. Each list element has to be present in this dictionary.

Example:
A movie has a list of actors.

Now I want a list of movies the actor acted in. If the movies have more than one actor, each actor should have the movie in their own list.

I have a data strucuture like this:

public class MovieData
{
    public List<string> Actors { get; set;}
    public string Title { get; set; }
    public DateTime ReleaseDate { get; set; }
    public string FilePath { get; set; }
}

A small example to illustrate this:

var movie1 = new MovieData { 
  Title  = "Movie1", 
  Actors = new List<string> { "Bob Smith", "Alice Stevens"}};

var movie2 = new MovieData { 
  Title  = "Movie2", 
  Actors = new List<string> { "Alice Stevens"}};

var movie3 = new MovieData { 
  Title  = "Movie3", 
  Actors = new List<string> { "John Doe" }};

The desired result should look like this:

Alice Stevens => { "Movie1", "Movie2" }
Bob Smith     => { "Movie1" }
John Doe      => { "Movie3" }

I came up with the following code, but it only pays attention to the first actor in the list (FirstOrDefault()). So other actors in the list are ignored.

var actorMovieList = movieDataList
  .ToLookup(movie => movie
                       .Actors
                       .FirstOrDefault(), movie => movie);

How can I include the other actors in the list? I have to iterate through the actor list inside of the lambda, but I don't know how.

4 Answers

You can try SelectMany to flatten the initial List<List<string>> into

{actor, title}

records and then put GroupBy to group them by actor into required dictionary:

var result = movieDataList
  .SelectMany(movie => movie
     .Actors
     .Select(actor => new {    // from now we have flat cursor of
        actor = actor,         // {actor, title} records
        title = movie.Title
      }))
  .GroupBy(record => record.actor,  // ... which we group by actor: 
           record => record.title)  // actor and movies (s)he played in 
  .ToDictionary(group => group.Key, // or ToLookUp() 
                group => group.ToList());

You can flatten the movieDataList first (using SelectMany method) into collection of actors and movie titles and then use ToLookup method with this collection. It allows you to include all authors in the final result

var movieDataList = new List<MovieData>() { movie1, movie2, movie3 };

var result = movieDataList
    .SelectMany(m => m.Actors
        .Select(a => new
        {
            Actor = a,
            Movie = m.Title
        }))
    .ToLookup(pair => pair.Actor, pair => pair.Movie);

foreach (var item in result)
{
    Console.WriteLine($"{item.Key} {string.Join(",", item)}");
}

The output will be the following

enter image description here

Here is a quick solution which provides the desired results:

Let's say you have a movie data list like this:

var movieDataList = new List<MovieData> { movie1, movie2, movie3 };

First, let's extract all the actors:

// NOTE: Used ToHashSet() in the end to avoid the duplicates in the names
var actors = movieDataList
    .SelectMany(item => item.Actors)
    .ToHashSet();

Now let's craft the dictionary which contains the name of the actor and list of movies

var allActorMovieList = new Dictionary<string, List<string>>();
foreach (var actor in actors)
{
    // List of the movies of the actor
    var actorMovieList = movieDataList
        .Where(movieData => movieData.Actors.Contains(actor))
        .Select(movieData => movieData.Title)
        .ToList();

    allActorMovieList.Add(actor, actorMovieList);
}

Now allActorMovieList contains all the actors with their films

var flatQuery = 
  from movie in movies
  from actor in movie.Actors
  select new {movie, actor};

var lookup = flatQuery.ToLookup(
  pair => pair.actor,
  pair => pair.movie.Title);

var aliceStevensMovies = lookup["Alice Stevens"].ToList();
var amyBMovies = lookup["Amy B"].ToList(); //empty, not keyNotFound
Related