0

I have a string that has all the available partition letters for allocation.

I want to delete each character (or drive letter) that is already in use from this string.

I have tried doing this:

string allletters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
DriveInfo[] d = DriveInfo.GetDrives();


string notusedletters = string.Empty;

foreach (DriveInfo drive in AllDrives)
{
    string driveLetterOnly = (drive.Name).Replace(@":\", ""); // remove ":\" characters from the drive letter
    notusedletters = allletters.Replace(driveLetterOnly, ""); // remove driveLetterOnly from the allletters string.
}

But notusedletters string always returns the initial value of allletters (ABCDEFGHIJKLMNOPQRSTUVWXYZ).

What's wrong with this code?

PravinS
  • 2,640
  • 3
  • 21
  • 25
alexenv
  • 93
  • 1
  • 2
  • 9

2 Answers2

2

When you call a Replace on allletters a new string will be created. Replace does not modify exists string. To make your code work you should re-assign allletters on each iteration:

allletters = allletters.Replace(....)

Or just use this single line linq query:

string allletters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
string notusedletters = new string(
  allletters.Except(
    DriveInfo.GetDrives().Select(d => d.Name[0])
  ).ToArray());


From comments:

It should be noted that Except does not guarantee that the order is preserved

I've checked this answer and it says that Except preserves order. To make sure I've decompiled System.Core

Please see implementation of Except with my comments:

public static IEnumerable<TSource> Except<TSource>(
  this IEnumerable<TSource> first,
  IEnumerable<TSource> second)
{
  // I removed a null checks
  return Enumerable.ExceptIterator<TSource>(first, second,
    (IEqualityComparer<TSource>) null);
}

private static IEnumerable<TSource> ExceptIterator<TSource>(
  IEnumerable<TSource> first,
  IEnumerable<TSource> second,
  IEqualityComparer<TSource> comparer)
{
  Set<TSource> set = new Set<TSource>(comparer);
  foreach (TSource source in second)
    set.Add(source);
  foreach (TSource source in first)
  {
    if (set.Add(source))
      yield return source; // elements will be yielded in same order
                           // as they appear in first sequence
  }
}

But to be 100% sure that behavior will not change in future you can use OrderBy as @ckuri suggested in comment:

string notusedletters = new string(
    allletters.Except(
      DriveInfo.GetDrives().Select(d => d.Name[0])
    )
    .OrderBy(c => c) // explicit ordering
    .ToArray()
  ); 
Aleks Andreev
  • 7,016
  • 8
  • 29
  • 37
  • It should be noted that Except does not guarantee that the order is preserved. There fore if the original order is required an OrderBy might be needed too. – ckuri Jun 30 '18 at 20:55
1

Do not use notusedletters or just keep the same string while using the replace.

string allletters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
DriveInfo[] d = DriveInfo.GetDrives();

foreach (DriveInfo drive in d)
{
    string driveLetterOnly = (drive.Name).Replace(@":\", ""); // remove ":\" characters from the drive letter
    allletters = allletters.Replace(driveLetterOnly, ""); // remove driveLetterOnly from the allletters string.
}
Console.WriteLine(allletters);
L_J
  • 2,351
  • 10
  • 23
  • 28