简体   繁体   中英

C# - Adding indexes of specific chars in a string to a list of integers

Looking for guidance on how I can get the code below to produce a list of indexes of every white-space and hyphen character in longString .

Whenever I run the statements below, it just keeps adding 5 to my integer list. Given the string I'm using as an example below, I'm trying to have it collect 5, 11, 14, 24, and 27.

static void Main(string[] args)
    {
        string longString = "hello world my user-name is stevieray8450";
        int longStringLength = longString.Length;
        char whiteSpace = ' ';
        char hyphen = '-';

        // list to store all indexes of white space
        List<int> specialIndexes = new List<int>();

        foreach (char c in longString)
        {
            if (c.Equals(whiteSpace) || c.Equals(hyphen))
            {
                specialIndexes.Add(longString.IndexOf(c));
            }
        }

I'm racking my brain as to why the char c in my foreach loop is evaluating to 5 every time for white-space in my specialIndexes.Add(longString.IndexOf(c)); statement.

Any thoughts, opinions, comments welcome :) Thanks!

You should use a for loop instead so you can directly get the index:

for(int i =0; i < longString.length; i++)
{
    if (longString[i].Equals(whiteSpace) || longString[i].Equals(hyphen))
    {
         specialIndexes.Add(i);
    }
}

Of course, someone will get you some LINQ code that will do the same thing in much less code. Wait for it...

longString.IndexOf(c) when your c = ' ' will always evaluate to 5. IndexOf just returns the index of the first occurrence of its argument.

public int IndexOf(char value)

The zero-based index position of value if that character is found, or -1 if it is not.

Remember that char is a value-type so by default you are working with a copy.

When you call index of, you will get the index of the first match in the string

https://msdn.microsoft.com/en-us/library/k8b1470s(v=vs.110).aspx

you can try with:

string longString = "hello world my user-name is stevieray8450";
int longStringLength = longString.Length;
char whiteSpace = ' ';
char hyphen = '-';
int index = 0;

// list to store all indexes of white space
List<int> specialIndexes = new List<int>();

foreach (char c in longString)
{
    if (c.Equals(whiteSpace) || c.Equals(hyphen))
    {
        specialIndexes.Add(index);
    }
    index++;
}

The following line...

specialIndexes.Add(longString.IndexOf(c));

...will return the index of the first occurrence of c, not the current one. In the sample string, the first space or hyphen is the space at position 5.

The best way to solve this would be with a for loop, and store the counter value as the index.

It is because the char type is a value type and also IndexOf returns the index of the first occurrence only. That said, it will return 5 as the result.

You need to add your own counter into the foreach loop or loop in a for and use its index there.

static void Main(string[] args) {
    string longString = "hello world my user-name is stevieray8450";
    HashSet<char> desiredChars = new HashSet<char> { ' ', '-' };

    List<int> specialIndexes = longString
       .Select((char, i) => new KeyValuePair<char, int>(char, i))
       .Where(kvp => desiredChars.Contains(kvp.Key))
       .Select(kvp => kvp.Value)
       .ToList();
}

Here is another version of your code:

string longString = "hello world my user-name is stevieray8450";
int longStringLength = longString.Length;
char whiteSpace = ' ';
char hyphen = '-';

// list to store all indexes of white space
List<int> specialIndexes = new List<int>();

int index = 0;
do
{
       index = longString.IndexOf(whiteSpace, index);
       if (index > -1)
       {
             specialIndexes.Add(index);
             index++;
       }

} while (index != -1);

I prefer LINQ solutions where I can use them. The problem with LINQ is that indexes do not carry forward to the next statement. To see what I mean consider the following:

var longString = "hello world my user-name is stevieray8450";

var badIndices = longString
    .Where(c => c == ' ')
    .Select((c, i) => i)
    .ToArray();

Console.WriteLine($"[bad!] found ' ' at indices: {string.Join(", ", badIndices)}");

While it seems the result should be 5, 11, 14, 24, 27 , the actual result will be:

[bad!] found ' ' at indices: 0, 1, 2, 3, 4

In order to carry forward the correct index, we have to create an intermediate anonymous type on the fly:

    var indices = longString
        .Select((c, i) => new
        {
            idx = i,
            val = c
        })
        .Where(ci => ci.val == ' ')
        .Select(ci => ci.idx)
        .ToArray();

    Console.WriteLine($"[good] found ' ' at indices: {string.Join(", ", indices)}");

It's not that bad once you get used to it and I prefer it over writing my own loops. The results are:

[good] found ' ' at indices: 5, 11, 14, 24, 27

However, in this particular case, the for-loop is much more efficient and completes in about 1/5th the time as LINQ.

From this answer , you can use a for loop in conjunction with the (char, int) overload of IndexOf to put together a concise solution:

var specialIndexes = new List<int>();
for (int i = s.IndexOf(' '); i > -1; i = s.IndexOf(' ', i + 1))
{
    specialIndexes.Add(i);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM