3
votes

I currently have a class that uses the KeyValuePair with List to store a collection of tracks in the format of Key = track, Value = artist.

I'm trying to provide a way of searching for a particular track and if there are any matches then return the entire matching CD.

This is my attempt so far:

public CompilationCD FindTrackInComCD(string track)
{
    CompilationCD temp = new CompilationCD();

    List<CD> tempComCols = _cdCollection.FindAll(delegate(CD cd)
    { return cd.GetType() == temp.GetType(); });

    foreach (KeyValuePair<string, string> comCD in tempComCols)
    {
        if (comCD.Key.Contains(track))
        {
            return comCD;
        }
    }

    throw new ArgumentException("No matches found");
}

I have a collection of Cd's of type CD (List<CD>) therefore I create a new List<> of the appropiate type by comparing it to the temp List.

When compiling I get the following errors:

Cannot convert type 'CDCollection.CD' to System.Collections.Generic.KeyValuePair<string,string>'

Cannot implicitly convert type 'System.Collections.Generic.KeyValuePair<string,string>'

(CDCollection is my project namespace and CD/CompilationCD are the classes)

Sorry for this seeming like a similar question to one I've previously asked. I tried to use the methods I was given before but I'm a bit stumped; I've not used List<> or KeyValuePair very often.

This is the CD Class:

using System;

using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text;

namespace CDCollection { public class CD { #region Fields private readonly string _artist; private readonly string _album; private List _track = new List(); #endregion

    #region Constructors
    public CD()
    {
        _artist = "";
        _album = "";
        _track = null;
    }

    public CD(string albumName)
    {
        _album = albumName;
    }

    public CD(string artistName, string albumName)
    {
        _artist = artistName;
        _album = albumName;
    }

    #endregion

    #region Properties
    /// <summary>
    /// Get/Set Artist Name
    /// </summary>
    public virtual string Artist
    {
        get
        {
            return _artist;
        }
        set
        {
            value = _artist;
        }
    }

    /// <summary>
    /// Get/Set Album
    /// </summary>
    public string Album
    {
        get
        {
            return _album;
        }
        set
        {
            value = _album;
        }
    }

    /// <summary>
    /// Get/Set Track Name
    /// </summary>
    public virtual List<string> Track
    {
        get
        {
            return _track;
        }
        set
        {
            value = _track;
        }
    }

    #endregion

    #region ToString()
    /// <summary>
    /// Custom ToString() Method
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
        //Create new StringBuilder object
        StringBuilder sb = new StringBuilder();

        sb.Append("Artist Name");

        //Display error if Artist is not available
        if (_artist == null || _artist == "")
        {
            sb.Append("\nNo Artist Entered");
        }
        else
        {
            sb.Append("\n" + this._artist);
        }

        sb.Append("\n");
        sb.Append("\nAlbum Name");

        //Display error if Album is not available
        if (_album == null || _album == "")
        {
            sb.Append("\nNo Album Entered");
        }
        else
        {
            sb.Append("\n" + this._album);
        }

        sb.Append("\n");
        sb.Append("\nTrack Name");
        sb.Append("\n");

        //Iterate through all tracks stored in list
        foreach (string trackName in _track)
        {
            //Print each artist
            sb.Append("\n" + trackName);
        }

        sb.Append("\nEnd of CD Record.........");

        return sb.ToString();
    }

    #endregion
}

}

This is the CompilationCD class:

using System;

using System.Collections.Generic; using System.Linq; using System.Text;

namespace CDCollection { public class CompilationCD : CD { #region Fields

    private readonly string _artist;
    private readonly string _album;
    private List<KeyValuePair<string,string>> _tracks = new List<KeyValuePair<string,string>>();

    //List<KeyValuePair> Reference.
    //http://msdn.microsoft.com/en-us/library/6sh2ey19(VS.85).aspx

    #endregion

    #region Constructors

    public CompilationCD()
    {
        _album = "";
        _artist = "Various Artists";
    }

    public CompilationCD(string albumName):base(albumName)
    {
        _album = albumName;
        _artist = "Various Artists";
    }

    #endregion

    public void AddTracks(string track, string artist)
    {
        _tracks.Add(new KeyValuePair<string, string>(track, artist));
    }

    #region Properties

    public override string Artist
    {
        get
        {
            return this._artist;
        }
    }

    public new List<KeyValuePair<string,string>> Track
    {
        get
        {
            return _tracks;
        }
        set
        {
            _tracks = value;
        }
    }


    #endregion

    #region ToString()

    //TEST
    public override string ToString()
    {
        //Create new StringBuilder object
        StringBuilder sb = new StringBuilder();

        sb.Append("Artist Name");

        //Display error if Artist is not available
        if (_artist == null || _artist == "")
        {
            sb.Append("\nNo Artist Entered");
        }
        else
        {
            sb.Append("\n" + this._artist);
        }

        sb.Append("\n");
        sb.Append("\nAlbum Name");

        //Display error if Album is not available
        if (base.Album == null || base.Album == "")
        {
            sb.Append("\nNo Album Entered");
        }
        else
        {
            sb.Append("\n" + base.Album);
        }

        sb.Append("\n");
        sb.Append("\nTrack Name");
        sb.Append("\n");

        ////Iterate through all tracks stored in list
        //foreach (string trackName in base.Track)
        //{
        //    //Print each artist
        //    sb.Append("\n" + trackName);
        //}

        for(int i = 0; i <= _tracks.Count; i++)
        {
            string track = _tracks[i].Key;
            string artist = _tracks[i].Value;

            sb.Append("\nTrack");
            sb.Append(track);
            sb.Append("\nArtist");
            sb.Append(artist);
        }

        sb.Append("\nEnd of Compilation CD Record.........");

        return sb.ToString();
    }

    #endregion
}

}

I have strict rules that mean i have to inherit from CD to create my CompilationCD as well as using a List> for my track collection, it needs to hold both a track and artist. Crazy i know =/

Furthermore i must store ALL types of cd in a list of type CD ( List ).

8
You should consider using operator is instead of comparing the return values of GetType(). It's both faster, shorter, and plays better with extensibility (e.g. if you later derive some class from CompilationCD). - Pavel Minaev

8 Answers

2
votes

The problem is in your foreach loop. tempComCols is a List<CD>, but comCD is a KeyValuePair<string, string>. So, your loop results in an invalid type conversion.

Unfortunately, since we don't know what the CD class (interface?) looks like, I can't suggest a fix in terms of its properties.

EDIT: The following is perhaps a better version of your method (though, I haven't debugged it correctly):

public CompilationCD FindTrackInComCD(string track)
{
    CompilationCD temp = new CompilationCD();

    temp = _cdCollection.Where(cd => cd is CompilationCD)
                        .Cast<CompilationCD>()
                        .Where(com_cd => com_cd.Tracks.ContainsKey(track))
                        .FirstOrDefault();

    if (temp != null)
        return temp;
    else throw new ArgumentException("No matches found");
}

You can't cast a CompilationCD to a KeyValuePair<string, string>, so we just use the CompilationCD class directly. We can pass off the responsibility for searching the track list to the IEnumerable<T> extenion methods provided by System.Linq, which makes this method very easy.

5
votes

Why not use a dictionary? They are a list of key value pairs, but provide easy access via the key.

0
votes

that's because tempComCols will return CD and not KeyValuePair<string, string>

0
votes

Your List doesn't contain KeyValuePairs, so you can't loop through it as if it did. Try something like this:

foreach (CD comCD in tempComCols)
{
    if (comCD.MyKeyValueProp.Key.Contains(track))
    {
        return comCD;
    }
}

But as McKay said, if your CD class does nothing but encapsulate a KeyValuePair, a Dictionary would be much easier.

0
votes

tempComCols is list of CD items: List<CD> tempComCols ... and you want to iterate over something that is of type IEnumerable: foreach (KeyValuePair<string, string> comCD in tempComCols)

0
votes

You are enumerating a List<CD> and assigning to a KeyValuePair<string, string>.

You can rewrite your method using LINQ in C# 3 like this:

public CompilationCD FindTrackInComCD(string track) {
    return _cdCollection.OfType<CompilationCD>().FirstOrDefault(cd => cd.Name.Contains(track, StringComparison.CurrentCultureIgnoreCase));
}

By the way, you can get the System.Type object for CompilationCD by writing typeof(CompilationCD); you don't need to call GetType().

0
votes
  1. you can use:

    {cd is CompilationCD};
    

    instead of

    { return cd.GetType() == temp.GetType(); });
    
  2. if I understand correctly and CD is some sort of Dictionary, you can rewrite the function as:

    public CompilationCD FindTrackInComCD(string track)
    {
        return (CompilationCD)_cdCollection.Find(delegate(CD cd)
        { return (cd is CompilationCD) && (cd.Contains(track))});
    }