alexenv
alexenv

Reputation: 93

Delete specific characters in string C#

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?

Upvotes: 0

Views: 99

Answers (2)

Aleks Andreev
Aleks Andreev

Reputation: 7054

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()
  ); 

Upvotes: 2

L_J
L_J

Reputation: 2439

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);

Upvotes: 1

Related Questions