1

I have a class that I am trying to use in a file archiving app that I am writing, partly in order to get my head around generic collections and partly because we need an archiving app. I realise that there are simpler ways than using dictionaries to hold the information, but I would like to use this if I can.

I have used the Distinct() method when selecting using linq, but I cannot find the correct syntax for ensuring that concatenated columns are distinct - see my efforts below, which currently fail with the message that "A key with that value has already been added". Any suggestions would be really appreciated!

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

namespace Demo
{
    class Program
    {
        static void Main()
        {
            var allFilesToArchive = new List<MyFileInfo>();
            allFilesToArchive.Add(new MyFileInfo { FileName = "File1", ArchiveLocation = @"C:\Archive\", ArchiveName = "Archive1", ArchiveBackupLocation = @"C:\Backup\" });
            allFilesToArchive.Add(new MyFileInfo { FileName = "File2", ArchiveLocation = @"C:\Archive\", ArchiveName = "Archive1", ArchiveBackupLocation = @"C:\Backup\" });
            allFilesToArchive.Add(new MyFileInfo { FileName = "File3", ArchiveLocation = @"C:\Archive\", ArchiveName = "Archive2", ArchiveBackupLocation = @"C:\Backup\" });

            var archivePairs = new Dictionary<string, string>();
            try
            {
                archivePairs = allFilesToArchive.ToDictionary(o => o.ArchiveLocation + @"\" + o.ArchiveName
                        , o => (o.ArchiveBackupLocation == null ? "" : o.ArchiveBackupLocation)
                        + (o.ArchiveBackupLocation == null ? "" : @"\")
                        + (o.ArchiveBackupLocation == null ? "" : o.ArchiveName));

                //--------------------------------------fails------------------------------------------------
                //archivePairs = allFilesToArchive.ToDictionary(o => o.ArchiveLocation + @"\" + o.ArchiveName
                //        , o => (o.ArchiveBackupLocation == null ? "" : o.ArchiveBackupLocation)
                //        + (o.ArchiveBackupLocation == null ? "" : @"\")
                //        + (o.ArchiveBackupLocation == null ? "" : o.ArchiveName)).Distinct();

                //-----------------------------------Not Distinct over all columns-----------------------------------------------
                //archivePairs = allFilesToArchive.ToDictionary(o => o.ArchiveLocation.Distinct() + @"\" + o.ArchiveName.Distinct()
                //        , o => o.ArchiveBackupLocation.Distinct()
                //        + @"\"
                //        + o.ArchiveName.Distinct());
                foreach (var archivefile in archivePairs)
                {
                    Console.Write("archive {0} will be backed up to {1}", archivefile.Key, archivefile.Value);
                }
            }
            catch (Exception ex)
            {
                Console.Write("Balls up:" + ex.Message);
            }
        }
    }
    public class MyFileInfo
    {
        public string FileName { get; set; }
        public string ArchiveLocation { get; set; }
        public string ArchiveName { get; set; }
        public string ArchiveBackupLocation { get; set; }
    }
}

I am really having trouble inserting a distinct list of values from a concatenation of two columns with a low granularity - I have populated the list below and would like to have the dictionary archivePairs looking like this:

<table border="1px solid black">
  <tr>
    <th>Key</th>
    <th>Value</th> 
  </tr>
  <tr>
    <td>C:\Archive\Archive1</td>
    <td>C:\Backup\Archive1</td> 
  </tr>
  <tr>
    <td>C:\Archive\Archive1</td>
    <td>C:\Backup\Archive1</td> 
  </tr>
</table>
High Plains Grifter
  • 1,357
  • 1
  • 12
  • 36
  • yes, with a bit of editing, I have used the linked question to answer my question. the correct solution is this: ` var archivePairs = allFilesToArchive.GroupBy(o => o.ArchiveLocation + @"\" + o.ArchiveName, StringComparer.OrdinalIgnoreCase) .ToDictionary(p => p.Key, p => p.Last().ArchiveBackupLocation + @"\" + p.Last().ArchiveName, StringComparer.OrdinalIgnoreCase);` Now i just have to work out how the hell it works! – High Plains Grifter Jun 15 '18 at 14:29

1 Answers1

1

Instead of calling Distinct on strings, you should call Distinct on allFilesToArchive. Calling Distinct on a string will simply remove any character that appears more than once.

To tell Distinct how to tell if two MyFileInfos are distinct or not, we need to write an IEqualityComparer:

public class FileInfoEqualityComparer : IEqualityComparer<MyFileInfo> {
    public bool Equals(MyFileInfo info1, MyFileInfo info2) {
        return info1.ArchiveLocation + "\\" + info1.ArchiveName == info2.ArchiveLocation + "\\" + info2.ArchiveName;
    }

    public int GetHashCode(MyFileInfo info) {
        return (info.ArchiveLocation + "\\" + info.ArchiveName).GetHashCode();
    }
}

It's a comparer that only compares the archive location + archive name.

Now we pass an instance of this comparer to Distinct, then call ToDictionary:

var dict = allFilesToArchive.Distinct(new FileInfoEqualityComparer()).ToDictionary(o => o.ArchiveLocation + @"\" + o.ArchiveName
            , o => (o.ArchiveBackupLocation == null ? "" : o.ArchiveBackupLocation)
            + (o.ArchiveBackupLocation == null ? "" : @"\")
            + (o.ArchiveBackupLocation == null ? "" : o.ArchiveName));
Sweeper
  • 213,210
  • 22
  • 193
  • 313