Related
First array is filled randomly, its length is set from the console .I have an array in which I need to write a sub-array of repeating numbers and the number of repeating numbers. For example {3 3 3 3 3 4}, 3 3 3 3 is the repeating numbers, 4 is their number.
The problem is that if there are repeating numbers at the end of the array, the loop doesn't output them and if I enter the debug mode, I can see that not all numbers are written. What could be the problem?
for (int i = 1; i < array.Length; i++)
{
if (array[i - 1] == array[i])
{
if((i + 1) != array.Length)
{
duplicateCount++;
addArray = array[i - 1];
duplicate += addArray + " ";
}
else
{
duplicateCount++;
lastElement = array[i - 1];
duplicate += lastElement;
}
}
else
{
if (duplicateCount != 1)
{
addPrevious = array[i - 1];
duplicate += addPrevious + " ";
duplicateArrays.Add(duplicate + duplicateCount);
duplicate = "";
duplicateCount = 1;
}
else
{
duplicateCount = 1;
}
}
}
duplicateArrays.Add(duplicate + duplicateCount);
While the Antidisestablishmentarianism code is the shortest one, it uses '^' which is index from end operator introduced in C# 8.0. I'm not carping about that but it's a difference worth knowing. Compiling the same code in the older version will generate a compile time exception. I'll be giving a long yet beginner friendly code which is pretty self-explanatory if you read it twice.
static void Main(string[] args)
{
int[] RandomArray = new int[] { 1, 2, 2, 3, 4, 4, 3, 1, 5, 2, 9, 8, 9, 8, 8, 5, 3, 4, 1 };
int RandomArrayLength = RandomArray.Length;
List<int[]> SubArrayList = new List<int[]>();
List<int> visited = new List<int>();
for (int i = 0; i < RandomArrayLength; i++)
{
int elem = RandomArray[i];
if (!visited.Contains(elem))
{
visited.Add(elem);
List<int> templist = new List<int>();
for (int j = 0; j < RandomArrayLength; j++)
{
if (elem == RandomArray[j])
{
templist.Add(elem);
}
}
if (templist.Count > 1) //You can remove this condition if you want to include all the elements
{
int elemCount = templist.Count;
List<int> sublist = new List<int>();
sublist.AddRange(templist);
sublist.Add(elemCount);
SubArrayList.Add(sublist.ToArray());
}
}
else
{
continue;
}
}
int SubArrayListCount = SubArrayList.Count;
for (int i = 0; i < SubArrayListCount; i++)
{
int[] SubArr = SubArrList[i];
int SubArrCount = SubArr.Length;
for (int j = 0; j < SubArrCount; j++)
{
Console.Write(SubArr[j].ToString() + " ");
}
Console.WriteLine();
}
}
I want to write a program that finds the longest sequence of equal elements in an array of integers. If several longest sequences exist, we should print the leftmost one. e.g. Input: 0 1 1 5 2 2 6 3 3
Output: 1 1
I know that my code doesn't work correctly, but I don't know how to fix it. I should solve the problem using only arrays because I don't know how to use lists.
int[] numbers = Console.ReadLine().Split().Select(int.Parse).ToArray();
for (int i = 0; i < numbers.Length; i++)
{
int[] currentSequenceOfEqualElements = new int[numbers.Length];
for (int j = i + 1; j < numbers.Length; j++)
{
if (numbers[i] == numbers[j])
{
if (currentSequenceOfEqualElements[0] == 0)
{
currentSequenceOfEqualElements[0] = numbers[i];
currentSequenceOfEqualElements[1] = numbers[i];
}
else
{
currentSequenceOfEqualElements[i + 2] = numbers[i];
}
}
else
{
break;
}
}
Console.WriteLine(string.Join(' ', currentSequenceOfEqualElements));
}
I will be very grateful if you can explain to me how to do it.
Here is the solution using the MoreLinq library (https://morelinq.github.io/) that mjwills suggested.
Once you get used to linq and morelinq methods the code is easier to understand than custom algo with nested loops and if.
var numbers = new int[]{ 0, 1, 1, 5, 2, 2, 6, 3, 3};
var result = numbers.GroupAdjacent(x => x)
.MaxBy(x => x.Count())
.FirstOrDefault();
foreach (var i in result)
{
Console.Write($"{i} ");
}
Here's a simple solution, using only loops and no linq. It should be nice and easy to understand.
int[] numbers = new[] { 0, 1, 1, 5, 2, 2, 6, 3, 3 };
// Some variables to keep track of the sequence we're currently looking
// at, and the longest sequence we've found so far. We're going to start
// the loop at the 2nd number, so we'll initialize these as if we've
// already processed the first number (which is 0, so we've seen the
// first number of a sequence of 0's).
// Number of numbers in the current sequence
int count = 1;
// Number which is part of the longest sequence so faar
int longestNum = numbers[0];
// Number of numbers in the longest sequence we've seen so far
int longestCount = 1;
for (int i = 1; i < numbers.Length; i++)
{
// We're starting a new sequence
if (numbers[i] != numbers[i-1])
{
count = 0;
}
count++;
// Have we just found a new longest sequence?
if (count > longestCount)
{
longestCount = count;
longestNum = numbers[i];
}
}
// longestNum = 1 and longestCount = 2 (because the longest sequence
// had 2 1's in it). Turn this into the string "1 1".
Console.WriteLine(
string.Join(" ", Enumerable.Repeat(longestNum, longestCount)));
// If you wanted to end up with an array containing [1, 1], then:
int[] result = new int[longestCount];
Array.Fill(result, longestNum);
I will illustrate a recursive answer for your question, below is the code, I kept some if-else statements that there is no need to have them, but at least the code shows the idea.
The code has a basic method that should be exposed as public and a private recursive method that does the heavy lifting. The longest sequence is the empty array(list)
var longSequenceEqualElem = new List<int>();
Later on the recursion, you pass all the elems of the array through all the recursion calls to keep querying the positions, the pos parameter indicates the position level of the recursion.
if (pos < elems.Length) //stop the recursion here, the position will fall out of the indexes of the array, just return what you have in sequence that should be the longest.
The following statement if (sequence.Contains(elems[pos])) means that you found the same number you were carrying on the sequence in the position pos, so you can add it to the sequence and call the recursion with the adjacent position(pos + 1)
If the element in position pos is not part of the sequence you had, then you need to call the recursion with a new sequence containing elems[pos] and later compare the result of that recursion call with the sequence you had to see which of them is the longest one.
Hope this helps
class Program
{
static void Main(string[] args)
{
var elemts = new int[] { 0, 1, 1, 5, 2, 2, 6, 3, 3 };
var result = LongestSequence(elemts);
foreach (var i in result)
{
Console.Write(i + "\t");
}
Console.ReadLine();
}
public static int[] LongestSequence(int[] elems)
{
var longSequenceEqualElem = new List<int>();
return LongestSequenceRec(elems, longSequenceEqualElem, 0);
}
private static int[] LongestSequenceRec(int[] elems, List<int> sequence, int pos)
{
if (pos < elems.Length)
{
if (sequence.Contains(elems[pos]))
{
sequence.Add(elems[pos]);
return LongestSequenceRec(elems, sequence, pos + 1);
}
else
{
var newSeq = LongestSequenceRec(elems, new List<int> { elems[pos] }, pos + 1);
return (newSeq.Length > sequence.Count) ? newSeq.ToArray() : sequence.ToArray();
}
}
return sequence.ToArray();
}
}
static void Main()
{
int[] array1 = new int[9] {0, 1, 1, 5, 2, 2, 6, 3, 3};
int[] array2 = new int[9] {0, 0, 0, 0, 0, 0, 0, 0, 0};
int max_count = 1;
int tempCount = 1;
int num = 0;
for (int i = 0; i < array1.Length - 1; i++)
{
if (array1[i] == array1[i + 1]) tempCount++;
else tempCount = 1;
if (tempCount > max_count)
{
max_count = tempCount;
num = array1[i];
}
}
for (int i = 0; i < max_count; i++) array2[i] = num;
for (int i = 0; i < max_count; i++) Console.Write(array2[i] + " ");
Console.ReadKey();
}
I want to find the total number of digits divisible by 5 between 1 - 100, in C# windows form, how to proceed from here?
int sum;
private void button2_Click(object sender, EventArgs e)
{
int[] intarray = new int[100];
for (int i = 0; i < 99; i++)
{
intarray[i] = i + 1;
}
foreach (int a in intarray)
{
if (a / 5 == 0)
{
}
}
}
Note than a / 5 == 0 is wrong. For example 10 is divisible by 2, the result is 10/5 = 2, not equal to 0.
if (a % 5 == 0)
{
//then a is divisible by 5. print or store it
}
The modulus operator, also known as Remainder, returns the remainder of the integer division.
Therefore, the full answer:
int nInRange = 0;
foreach (int a in intarray)
if (a % 5 == 0)
nInRange++;
Maybe this is what you want.
public static IEnumerable<int> GetIntsDivisible(int start, int finish, int divisor)
{
for (var i = start; i <= finish; i++)
if (i % divisor == 0)
yield return i;
}
public static void Main()
{
Console.WriteLine(string.Join(", ", GetIntsDivisible(1, 100, 5)));
}
or if you don't want to yield
public static List<int> GetIntsDivisible(int start, int finish, int divisor)
{
var result = new List<int>();
for (var i = start; i <= finish; i++)
if (i % divisor == 0)
result.Add(i);
return result;
}
Output
5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100
Full demo here
Initialize a variable as 0
for e.g. Count = 0;
then add statement Count++ in your if block as follows:
if (a % 5 == 0)
{
Count++;
}
Since question is a bit vague in exact requirement, I will write the basic logic that can find the numbers exactly divisible by other number.
There is something called modulus (%) operator. It gives you the remainder of division.e.g. 11%5 will be 1, 13%5 will be 3, whereas 15%5 will be 0
so logic goes like,
for(int i=0;i<=100;i++)
{
if((i%5)==0)
{
\\this is ur number
}
}
Every one has address your question but no one talk about this weird attempt.
I really think you should take 5 minute and read your code because you are just running everywhere.
You should take the pen and paper before going head first into coding.
Here is a simple reading of your code so you understand what you were doing.
Line 1:
int[] intarray = new int[100];
So you start win an array, I guess it's for the result, right ? You will not be storing the number from 1 to 100 for no reason?
Line 2:
for (int i = 0; i < 99; i++)
Now we count from 0 to 98, I though it was form 1 to 100 .. Yes 98 as you are using < instead of <=
Line 3:
intarray[i] = i + 1;
Why ? 3rd line and you are already lost! You are filling the array with number you just iterate. It's like filling a bottle with water, then use it to fill an other bottle because you needed water.
If you iterate from 1 to 100 you could have check if it was divisible.
for (int i = 1; i <= 100; i++)
Line 4:
foreach (int a in intarray)
Again ? We are back counting from 1 to 100..
Line 5:
if (a / 5 == 0)
If this is suppose to tell you if it divisible thats wrong. The correct math operator is the Modulo. The division symbole won't give you the result you expect.
{1,2,3,4} will give you True. Anything else will be false.
int count = 0;
for (int i = 1; i <= 100; i++)
{
if ((i % 5) == 0)
{
count++;
textBox1.Text = count.ToString();
}
}
Alternatively,
int num = (100 + 5) / 5 - (1 + 5 - 1) / 5;
//Show result here
I'm trying to calculate the number of possible combinations so I'm using some maths here (to be precise factorials). For example, if I have 50 numbers and I want to organize them into groups of 5, how many groups (combinations) are possible to make. I'm using this formula: allNumbers! / (allNumbers - PerGroup)!, but it comes up with an error for this particular example. It says that dividing by zero is forbidden. How can I manage this to work?
This is my code:
int b = 1;
int n = 1;
if (allNumbers - PerGroup == 0)
{
return 1;
}
else if (allNumbers - PerGroup == 1)
{
return allNumbers;
}
else
{
for (int i = 1; i <= allNumbers; i++)
{
b *= i;
}
for (int i = 1; i <= allNumbers - PerGroup; i++)
{
n *= i;
}
if (Enumerable.Range(1,int.MaxValue).Contains(b/n)) //line with ERROR!
{
return b/n;
}
else
{
return int.MaxValue;
}
}
Enumerable.Range(1,int.MaxValue).Contains(b/n) check doesn't check if the value is valid, because b/n is already computed and stored as int by this time.
You get division by zero because variable n is overflowed and becomes zero. In the following code you can see how overflow occurs.
using System;
public class Test
{
public static void Main()
{
int n = 1;
for (int i = 1; i <= 50; i++) {
n *= i;
Console.WriteLine("i = {0}, n = {1}", i, n);
}
}
}
Output:
i = 1, n = 1
i = 2, n = 2
i = 3, n = 6
i = 4, n = 24
i = 5, n = 120
i = 6, n = 720
i = 7, n = 5040
i = 8, n = 40320
i = 9, n = 362880
i = 10, n = 3628800
i = 11, n = 39916800
i = 12, n = 479001600
i = 13, n = 1932053504
i = 14, n = 1278945280
i = 15, n = 2004310016
i = 16, n = 2004189184
i = 17, n = -288522240
i = 18, n = -898433024
i = 19, n = 109641728
i = 20, n = -2102132736
i = 21, n = -1195114496
i = 22, n = -522715136
i = 23, n = 862453760
i = 24, n = -775946240
i = 25, n = 2076180480
i = 26, n = -1853882368
i = 27, n = 1484783616
i = 28, n = -1375731712
i = 29, n = -1241513984
i = 30, n = 1409286144
i = 31, n = 738197504
i = 32, n = -2147483648
i = 33, n = -2147483648
i = 34, n = 0
i = 35, n = 0
i = 36, n = 0
i = 37, n = 0
i = 38, n = 0
i = 39, n = 0
i = 40, n = 0
i = 41, n = 0
i = 42, n = 0
i = 43, n = 0
i = 44, n = 0
i = 45, n = 0
i = 46, n = 0
i = 47, n = 0
i = 48, n = 0
i = 49, n = 0
i = 50, n = 0
since allNumbers! always contains (allNumbers - PerGroup)!, why don't you exclude them from start.
int b = 1;
if (allNumbers - PerGroup == 0)
{
return 1;
}
else if (allNumbers - PerGroup == 1)
{
return allNumbers;
}
else
{
for (int i = (allNumbers - PerGroup + 1); i <= allNumbers; i++)
{
b *= i;
}
return b;
}
I guess the error is OutOfMemoryException because you are creating a huge amount of unnecessary integers. (Enumerable.Range(1,int.MaxValue)) note that each int takes 4 bytes from your memory.
Im not sure what you are trying to do there but you can use double type so if the number becomes very large it will just give you PositiveInfinity.
Or you can use checked to control integer overflow with try and catch.
Another way is to pre calculate the number when integer overflow happens. for example factorial of 14 will overflow for int and factorial of 22 will overflow for long.
Also you dont have to write for loop twice. you can use method for this purpose.
You dont need to check for allNumbers - PerGroup == 0 to prevent zero division. that wont happen because factorial of 0 is 1 and our factorial implementation returns 1 by its nature when the input is 0! (because the for loop never iterates in that case and the counter starts from 1.)
private static int Cominations(int allNumbers, int perGroup)
{
if(allNumbers > 13)
{
Console.WriteLine("Too big number!");
return -1;
}
return Factorial(allNumbers)/Factorial(allNumbers - perGroup);
}
private static int Factorial(int number)
{
int n = 1;
for (int i = 1; i < number; i++)
{
n *= i;
}
return n; // returns 1 when number is 0
}
If you want to calculate factorial of bigger numbers use BigInteger type from System.Numberics namespace.
using System.Numerics;
//...
private static BigInteger Factorial(int number)
{
BigInteger n = 1;
for (int i = 1; i < number; i++)
{
n *= i;
}
return n;
}
I have a string denoting page nos like 1,2,3,4,8,9,10,15.
I want this to be shown as 1-4,8-10,15 i.e numbers in sequence are separated by hyphen enclosed by smallest and largest number in sequence.
If break in sequence, the range is to be separated by comma.
string pageNos = "5,6,7,9,10,11,12,15,16";
string result=string.Empty;
string[] arr1 = pageNos.Split(',');
int[] arr = new int[arr1.Length];
for (int x = 0; x < arr1.Length; x++) // Convert string array to integer array
{
arr[x] = Convert.ToInt32(arr1[x].ToString());
}
for (int i = 0; i < arr.Length;i++)
{
for (int j = i + 1; ; j++)
if (arr[i] == (arr[j] - 1))
result += arr[i].ToString() + "-" + arr[j].ToString();
else
result += arr[i].ToString() + ",";
}
Console.WriteLine(result);
I think the loop-within-loop is making things more confusing. Try using just a single loop, because you only need to iterate over the entire list once.
int start,end; // track start and end
end = start = arr[0];
for (int i = 1; i < arr.Length; i++)
{
// as long as entries are consecutive, move end forward
if (arr[i] == (arr[i - 1] + 1))
{
end = arr[i];
}
else
{
// when no longer consecutive, add group to result
// depending on whether start=end (single item) or not
if (start == end)
result += start + ",";
else if (end == (start + 1))
result += start + "," + end + ",";
else
result += start + "-" + end + ",";
start = end = arr[i];
}
}
// handle the final group
if (start == end)
result += start;
else
result += start + "-" + end;
Demo: http://ideone.com/7HdpS7
A little bit of LINQ will tidy this up:
static IEnumerable<Tuple<int, int>> GetRanges(IEnumerable<int> source)
{
bool started = false;
int rangeStart = 0, lastItem = 0;
foreach (int item in source)
{
if (!started)
{
rangeStart = lastItem = item;
started = true;
}
else if (item == lastItem + 1)
{
lastItem = item;
}
else
{
yield return new Tuple<int, int>(rangeStart, lastItem);
rangeStart = lastItem = item;
}
}
if (started)
{
yield return new Tuple<int, int>(rangeStart, lastItem);
}
}
static string FormatRange(Tuple<int, int> range)
{
string format = (range.Item1 == range.Item2) ? "{0:D}" : "{0:D}-{1:D}";
return string.Format(format, range.Item1, range.Item2);
}
string pageNos = "5,6,7,9,10,11,12,15,16";
int[] pageNumbers = Array.ConvertAll(pageNos.Split(','), Convert.ToInt32);
string result = string.Join(",", GetRanges(pageNumbers).Select(FormatRange));
You could use this method to get adjacent groups of numbers where each group is represented by a custom Range-class:
class Range
{
public int? Start { get; set; }
public int? End { get; set; }
}
private static IEnumerable<Range> getAdjacentRanges(IEnumerable<int> nums)
{
var ranges = new List<Range>();
if (!nums.Any())
return ranges;
var ordered = nums.OrderBy(i => i);
int lowest = ordered.First();
int last = lowest;
ranges.Add(new Range { Start = lowest });
foreach (int current in ordered)
{
lastRange = ranges[ranges.Count - 1];
if (current > last + 1)
{
lastRange.End = last;
ranges.Add(new Range { Start = current });
}
last = current;
}
return ranges;
}
The rest is easy:
var arr = new[] { 1, 2, 3, 4, 8, 9, 10, 15 };
var ranges = getAdjacentRanges(arr)
.Select(r => r.End.HasValue ? string.Format("{0}-{1}", r.Start, r.End) : r.Start.ToString());
Console.Write(string.Join(",", ranges));
output: 1-4,8-10,15
DEMO
string pageNos = "5,6,7,9,10,11,12,15,16";
string[] arr1 = pageNos.Split(',');
int[] arr = new int[arr1.Length];
for (int x = 0; x < arr1.Length; x++) // Convert string array to integer array
{
arr[x] = Convert.ToInt32(arr1[x]);
}
StringBuilder sb = new StringBuilder();
bool hyphenOpen = false;
for (int i = 0; i < arr.Length - 1; i++)
{
if (arr[i] + 1 == arr[i+1])
{
if (!hyphenOpen)
{
hyphenOpen = true;
sb.Append(arr[i] + "-");
}
}
else
{
hyphenOpen = false;
sb.Append(arr[i] + ",");
}
}
sb.Append(arr[arr.Length-1]);
Console.WriteLine(sb.ToString());
This is long and clunky, but it works.
P.S. - I left the OP's original string->int as is, see comment by JonB on question for much cleaner method.
The following JS code will also help
Remove duplicates
Sort the array
Use 2 pointers
Start at the same place
Advance the second pointer until its next element is a successor
Print the element at i and j with spaces and comma
Don’t print element at j if the index are same
Remove the trailing comma
const givenArray = [1, 6, 6, 8, 44, 45, 47, 55, 9, 11, 12, 1, 6, 88, 13, 14, 2, 3, 5, 22, 33, 57, 88];
const input = [...new Set(givenArray)].sort((a, b) => a - b);
let i = 0;
let j = 0;
let output = '';
while (i < input.length) {
while (j < input.length && (input[j] + 1) === input[j + 1]) {
j++;
}
output += `${input[i]}`;
if (i !== j) {
output += ` - ${input[j]}, `;
} else {
output += ', ';
}
i = j + 1;
j = i;
}
console.log(output.substring(0, output.lastIndexOf(",")));
Use this helper class to convert back and forth between number lists and range strings.
This copies implementation of ConvertRangeStringToNumberList() from here and ConvertNumberListToRangeString() from here with slight improvements.
using System;
using System.Collections.Generic;
using System.Linq;
public static class NumberRangeHelper
{
/// <summary>
/// Converts a string of comma separated list of numbers and ranges to the list of individual numbers it represents.
/// </summary>
/// <param name="numbers">Range in form of <c>"2,4-8,11,15-22,39"</c></param>
/// <returns>A list of numbers</returns>
public static List<int> ConvertRangeStringToNumberList(string numbers)
{
var numbersSplit = numbers.Split(',');
var convertedNumbers = new SortedSet<int>();
foreach (var strNumber in numbersSplit)
{
int number;
if (int.TryParse(strNumber, out number))
{
convertedNumbers.Add(number);
}
else
{
// try and delimited by range
if (strNumber.Contains('-'))
{
var splitRange = strNumber.Split('-');
if (splitRange.Length == 2)
{
int firstNumber;
int secondNumber;
if (Int32.TryParse(splitRange[0], out firstNumber) &&
Int32.TryParse(splitRange[1], out secondNumber))
{
for (var i = firstNumber; i <= secondNumber; ++i)
{
convertedNumbers.Add(i);
}
}
}
}
}
}
return convertedNumbers.ToList();
}
/// <summary>
/// Converts a list of numbers to their concise range representation.
/// </summary>
/// <param name="numbers">A list of numbers such as <c>new[] { 1, 2, 3, 4, 5, 12, 13, 14, 19 }</c></param>
/// <returns>A string like <c>"1-5, 12-14, 19"</c></returns>
public static string ConvertNumberListToRangeString(IEnumerable<int> numbers)
{
var items = new SortedSet<int>(numbers)
.Select((n, i) => new { number = n, group = n - i })
.GroupBy(n => n.group)
.Select(g => (g.Count() >= 3)
? g.First().number + "-" + g.Last().number
: String.Join(", ", g.Select(x => x.number))
)
.ToList();
return String.Join(", ", items);
}
}
Test:
Action<IEnumerable<int>> DumpList = l => Console.WriteLine("\t[{0}]", String.Join(", ", l));
Action<string> DumpRange = s => Console.WriteLine("\t\"{0}\"", s);
var numbers = new[] { 1, 1, 2, 3, 4, 5, 12, 13, 19, 19, 6, 7 };
DumpList(numbers);
var str = ConvertNumberListToRangeString(numbers);
DumpRange(str);
var list = ConvertRangeStringToNumberList(str);
DumpList(list);
Console.WriteLine();
str = "1-5, 12, 13, 19, 20, 21, 2-7";
DumpRange(str);
list = ConvertRangeStringToNumberList(str);
DumpList(list);
str = ConvertNumberListToRangeString(list);
DumpRange(str);
Output:
[1, 1, 2, 3, 4, 5, 12, 13, 19, 19, 6, 7]
"1-7, 12, 13, 19"
[1, 2, 3, 4, 5, 6, 7, 12, 13, 19]
"1-5, 12, 13, 19, 20, 21, 2-7"
[1, 2, 3, 4, 5, 6, 7, 12, 13, 19, 20, 21]
"1-7, 12, 13, 19-21"
i am not a C# person, but i guess here you have problem:
if (arr[i] == (arr[j] - 1))
result += arr[i].ToString() + "-" + arr[j].ToString();
you shouldn't add that in your result. but set a flag (boolean maybe), to indicate that now I start counting.
if the flag==ture and the number is not continuous any longer, that is the time to add to your result, of course with "-".
public static string HyphenateRanges(this string input)
{
if (string.IsNullOrEmpty(input))
{
return "";
}
var orderedDistinct = input.Split(',')
.Select(Int32.Parse)
.Distinct()
.OrderBy(x => x)
.ToArray();
Func<int, int, string> replaceRangeValuesWithDash =
(x, i) =>
i == 0 || // first
i == orderedDistinct.Length - 1 || // last
orderedDistinct[i + 1] - orderedDistinct[i - 1] != 2 // not in a range
? x.ToString()
: "-";
var rangeValuesDashed = orderedDistinct
.Select(replaceRangeValuesWithDash)
.ToList();
var extraDashesRemoved = rangeValuesDashed
.Where((x, i) => i == 0 || rangeValuesDashed[i - 1] != x)
.ToArray();
var formattedString = String.Join(",", extraDashesRemoved)
.Replace(",-,", "-");
return formattedString;
}
Here's a different solution that creates a List<Tuple<int, int>> with each non-sequential value and the number of sequential values that follow it. This is then turned into a string using string.Join.
string pageNos = "1,2,3,4,8,9,10,15";
// Get list of numbers as ints
var list = pageNos.Split(',').Select(i => Convert.ToInt32(i)).ToList();
// Get a list of numbers and ranges of consecutive numbers
var ranges = new List<Tuple<int, int>>();
int start = 0;
for (int i = 0; i < list.Count; i++)
{
// First item always starts a new range
if (i == 0)
{
start = list[i];
}
// Last item always ends the current range
if (i == list.Count - 1)
{
if (list[i] == list[i - 1] + 1)
{
ranges.Add(new Tuple<int, int>(start, list[i] - start));
}
else
{
ranges.Add(new Tuple<int, int>(start, list[i - 1] - start));
ranges.Add(new Tuple<int, int>(list[i], 0));
}
}
// End the current range if nonsequential
if (i > 0 && i < list.Count - 1 && list[i] != list[i - 1] + 1)
{
ranges.Add(new Tuple<int, int>(start, list[i - 1] - start));
start = list[i];
}
}
// Craete the result string
var result = string.Join(", ", ranges.Select(r => r.Item2 == 0 ? r.Item1.ToString() : string.Format("{0}-{1}", r.Item1, r.Item1 + r.Item2)));