nop
nop

Reputation: 6323

Shortening a string of numbers

I have the following sequence of numbers:

enter image description here

You can see that those numbers a lot. I want to shorten that string. Let's say if the string contains more than 20 numbers, it should display 18 numbers, then "..." and then the last two of the sequence.

I could probably do that by adding those numbers in a List<int> or HashSet<int> (HashSet might be faster in this case), but I think it will be slow.

StringBuilder temp = new StringBuilder();

for (...)
{
    temp.Append($"{number} ");
}

var sequence = temp.ToString();

Example of what I want:

7 9 12 16 18 21 25 27 30 34 36 39 43 45 48 52 54 57 ... 952 954

Note that I want only fast ways.

Upvotes: 3

Views: 1146

Answers (6)

user1945782
user1945782

Reputation:

There is an alternative way that prevents iteration through the entire string of numbers and is reasonably fast.

Strings in .NET are basically an array of chars, and can be referenced on an individual basis using array referencing ([1..n]). This can be used to our advantage by simply testing for the correct number of spaces from the start and end respectively.

There are no niceties in the code, but they could be optimised later (for instance, by ensuring that there's actually something in the string, that the string is trimmed etc.).

The functions below could also be optimised to a single function if you're feeling energetic.

string finalNumbers = GetStartNumbers(myListOfNumbers, 18);
if(finalNumbers.EndsWith(" ... "))
    finalNumbers += GetEndNumbers(myListOfNumbers, 2);


public string GetStartNumbers(string listOfNumbers, int collectThisManyNumbers) 
{
    int spaceCounter = 0;    //  The current count of spaces
    int charPointer = 0;     //  The current character in the string
    //  Loop through the list of numbers until we either run out of characters
    //  or get to the appropriate 'space' position...
    while(spaceCounter < collectThisManyNumbers && charPointer <= listOfNumbers.Length)
    {
        //  The following line will add 1 to spaceCounter if the character at the
        //  charPointer position is a space. The charPointer is then incremented...
        spaceCounter += ( listOfNumbers[charPointer++]==' ' ? 1 : 0 );
    }
    //  Now return our value based on the last value of charPointer. Note that
    //  if our string doesn't have the right number of elements, then it will
    //  not be suffixed with ' ... '
    if(spaceCounter < collectThisManyNumbers) 
        return listOfNumbers.Substring(0, charPointer - 1);
    else
        return listOfNumbers.Substring(0, charPointer - 1) + " ... ";
}

public string GetEndNumbers(string listOfNumbers, int collectThisManyNumbers) 
{
    int spaceCounter = 0;                       //  The current count of spaces
    int charPointer = listOfNumbers.Length;     //  The current character in the string
    //  Loop through the list of numbers until we either run out of characters
    //  or get to the appropriate 'space' position...
    while(spaceCounter < collectThisManyNumbers && charPointer >= 0)
    {
        //  The following line will add 1 to spaceCounter if the character at the
        //  charPointer position is a space. The charPointer is then decremented...
        spaceCounter += ( listOfNumbers[charPointer--]==' ' ? 1 : 0 );
    }
    //  Now return our value based on the last value of charPointer...
    return listOfNumbers.Substring(charPointer);
}

Some people find the use of ++ and -- objectionable but it's up to you. If you want to do the maths and logic, knock yourself out!

Please note that this code is quite long because it's commented to the far end of a fart.

Upvotes: 0

Tim Rutter
Tim Rutter

Reputation: 4679

Try this:

public string Shorten(string str, int startCount, int endCount)
    {
        //first remove any leading or trailing whitespace
        str = str.Trim();

        //find the first startCount numbers by using IndexOf space
        //i.e. this counts the number of spaces from the start until startCount is achieved
        int spaceCount = 1;
        int startInd = str.IndexOf(' ');
        while (spaceCount < startCount && startInd > -1)
        {
            startInd = str.IndexOf(' ',startInd +1);
            spaceCount++;
        }

        //find the last endCount numbers by using LastIndexOf space
        //i.e. this counts the number of spaces from the end until endCount is achieved
        int lastSpaceCount = 1;
        int lastInd = str.LastIndexOf(' ');
        while (lastSpaceCount < endCount && lastInd > -1)
        {
            lastInd =  str.LastIndexOf(' ', lastInd - 1);
            lastSpaceCount++;
        }

        //if the start ind or end ind are -1 or if lastInd <= startIndjust return the str 
        //as its not long enough and so doesn't need shortening
        if (startInd == -1 || lastInd == -1 || lastInd <= startInd) return str;

        //otherwise return the required shortened string
        return $"{str.Substring(0, startInd)} ... {str.Substring(lastInd + 1)}";
    }

the output of this:

 Console.WriteLine(Shorten("123 123 123 123 123 123 123 123 123 123 123",4,3));

is:

123 123 123 123 ... 123 123 123

Upvotes: 0

yaakov
yaakov

Reputation: 5850

This version is about 8 times faster than the other answers and allocates only about 6% as much memory. I think you'll be hard-pressed to find a faster version:

static string Truncated(string input)
{
    var indexOfEighteenthSpace = IndexOfCharSeekFromStart(input, ' ', 18);
    if (indexOfEighteenthSpace <= 0) return input;

    var indexOfSecondLastSpace = IndexOfCharSeekFromEnd(input, ' ', 2);
    if (indexOfSecondLastSpace <= 0) return input;

    if (indexOfSecondLastSpace <= indexOfEighteenthSpace) return input;

    var leadingSegment = input.AsSpan().Slice(0, indexOfEighteenthSpace);
    var trailingSegment = input.AsSpan().Slice(indexOfSecondLastSpace + 1);

    return string.Concat(leadingSegment, " ... ", trailingSegment);

    static int IndexOfCharSeekFromStart(string input, char value, int count)
    {
       var startIndex = 0;
       for (var i = 0; i < count; i++)
       {
           startIndex = input.IndexOf(value, startIndex + 1);
           if (startIndex <= 0) return startIndex;
       }

        return startIndex;
    }

    static int IndexOfCharSeekFromEnd(string input, char value, int count)
    {
       var endIndex = input.Length - 1;
       for (var i = 0; i < count; i++)
       {
           endIndex = input.LastIndexOf(value, endIndex - 1);
           if (endIndex <= 0) return endIndex;
       }

        return endIndex;
    }
}   

Upvotes: 3

Issung
Issung

Reputation: 445

No idea what the speed on this would be like but as a wild suggestion, you said the numbers come in string format and it looks like they're seperated by spaces. You could get the index of the 19th space (to display 18 numbers) using any of the methods found here, and substring from index 0 to that index and concatenate 3 dots. Something like this:

numberListString.SubString(0, IndexOfNth(numberListString, ' ', 19)) + "..."

(Not accurate code, adding or subtracting indexes or adjusting values (19) may be required).

EDIT: Just saw that after the dots you wanted to have the last 2 numbers, you can use the same technique! Just concatenate that result again.

NOTE: I used this whacky technique because the OP said they wanted fast ways, I'm just offering a potential option to benchmark :)

Upvotes: 0

Flater
Flater

Reputation: 13783

Small individual steps

How do I make a list from this sequence (string)?

var myList = myOriginalSequence.Split(' ').ToList();

How do you take the first 18 numbers from a list?

var first18Numbers = myList.Take(18);

How do you take the last 2 numbers from a list?

var last2Numbers = myList.Skip(myList.Count() - 2);

How do you ensure that this is only done when there are more than 20 numbers in the list?

if(myList.Count() > 20)

How do you make a new sequence string from a list?

var myNewSequence = String.Join(" ", myList);

Putting it all together

var myList = myOriginalSequence.Split(' ').ToList();

string myNewSequence;

if(myList.Count() > 20)
{
    var first18Numbers = myList.Take(18);
    var first18NumbersString = String.Join(" ", first18Numbers);

    var last2Numbers = myList.Skip(myList.Count() - 2);
    var last2NumbersString = String.Join(" ", last2Numbers);

    myNewSequence = $"{first18NumbersString} ... {last2NumbersString}"
}
else
{
    myNewSequence = myOriginalSequence;
}

Console.WriteLine(myNewSequence);

Upvotes: 3

XavierAM
XavierAM

Reputation: 1775

I think this may help :

    public IEnumerable<string> ShortenList(string input)
    {
       List<int> list = input.Split(" ").Select(x=>int.Parse(x)).ToList();
        if (list.Count > 20)
        {
            List<string> trimmedStringList = list.Take(18).Select(x=>x.ToString()).ToList();
            trimmedStringList.Add("...");
            trimmedStringList.Add(list[list.Count-2].ToString());
            trimmedStringList.Add(list[list.Count - 1].ToString());

            return trimmedStringList;
        }

        return list.Select(x => x.ToString());
    }

Upvotes: 0

Related Questions