counting highest number of 0's between two 1's in c# - c#

I need help with a program that'll count the maximum number of 0's between two 1's in a given binary number in c#. For example 1100101(binary for 101) the maximum number of 0's between two 1's are 2. Any help?
This the code for counting the 0's in the string but not the 0's between 1's
string bin = "";
int max = 0;
for (int i = rem.Length - 1; i >= 0; i--)
{
if (rem[i] == '0')
{
bin = bin + rem[i];
c++;
}
else
{
bin = bin + rem[i];
}
}

Try this (updated code):
string num = "1011100";
char[] myChar = num.ToCharArray();
bool blFirst = false; //This will check if there is "1" on first element of the input
bool blLast = false; //This will check if there is "1" on last element of the input
if (myChar[0] == '0') //If the condition is true we will remove this on the result
blFirst = true;
if (myChar[myChar.Length - 1] == '0')
blLast = true;
string[] intArr = num.Split('1').ToArray();
List<string> intResult = new List<string>();
//We will make sure that all results only contains '0' and not empty.
intResult = intArr.Where(x => x.All(y => y == '0') && x != string.Empty).Select(x => x).ToList();
if (blFirst == true)
intResult.RemoveAt(0);
if (blLast == true)
intResult.RemoveAt(intResult.Count - 1);
//After all conditions are met (Get only '0' numbers between 1), that's the time we get the maximum count
intOutput = intResult.Select(x => x).Max(x => x.Length);

A simpler version:
string num = "1011100";
//trim the 0's at the start and the end
num=num.Trim(new Char[] { '0' });
string[] intArr = num.Split(new string[] {"1"}, StringSplitOptions.RemoveEmptyEntries);
int Output = intArr.Max(x => x.Length);

This should work:
public static int CountZerosBetweenOnes(string binary)
{
var indicesOfOnes =
binary.Select((c, i) => new {c, i})
.Where(x => x.c == '1')
.Select(x => x.i);
return
indicesOfOnes
.Zip(indicesOfOnes.Skip(1), (a, b) => b - a - 1)
.DefaultIfEmpty(0)
.Max();
}
It does assume that binary does not contain any characters other than 1 and 0.
If you want a version that accepts an int:
public static int CountZerosBetweenOnes(int binary)
{
var indicesOfOnes =
Convert.ToString(binary, 2)
.Select((c, i) => new {c, i})
.Where(x => x.c == '1')
.Select(x => x.i);
return
indicesOfOnes
.Zip(indicesOfOnes.Skip(1), (a, b) => b - a - 1)
.DefaultIfEmpty(0)
.Max();
}
I tested this with the following test code, which outputs the expected result:
public static void Main(string[] args)
{
test("0000000000000"); // Outputs 0
test("0000100010000"); // Outputs 3
test("1111111111111"); // Outputs 0
test("1000000000001"); // Outputs 11
test("1000000000000"); // Outputs 0
test("0000000000001"); // Outputs 0
test("1010101010101"); // Outputs 1
test("1001000100001"); // Outputs 4
}
static void test(string s)
{
Console.WriteLine(CountZerosBetweenOnes(s));
}
Or run it on .Net Fiddle here: https://dotnetfiddle.net/H2Mt8w

Related

c# ordering strings with different formats

I have Licence plate numbers which I return to UI and I want them ordered in asc order:
So let's say the input is as below:
1/12/13/2
1/12/11/3
1/12/12/2
1/12/12/1
My expected output is:
1/12/11/3
1/12/12/1
1/12/12/2
1/12/13/2
My current code which is working to do this is:
var orderedData = allLicenceNumbers
.OrderBy(x => x.LicenceNumber.Length)
.ThenBy(x => x.LicenceNumber)
.ToList();
However for another input sample as below:
4/032/004/2
4/032/004/9
4/032/004/3/A
4/032/004/3/B
4/032/004/11
I am getting the data returned as:
4/032/004/2
4/032/004/9
4/032/004/11
4/032/004/3/A
4/032/004/3/B
when what I need is:
4/032/004/2
4/032/004/3/A
4/032/004/3/B
4/032/004/9
4/032/004/11
Is there a better way I can order this simply to give correct result in both sample inputs or will I need to write a custom sort?
EDIT
It wont always be the same element on the string.
This could be example input:
2/3/5/1/A
1/4/6/7
1/3/8/9/B
1/3/8/9/A
1/5/6/7
Expected output would be:
1/3/8/9/A
1/3/8/9/B
1/4/6/7
1/5/6/7
2/3/5/1/A
You should split your numbers and compare each part with each other. Compare numbers by value and strings lexicographically.
var licenceNumbers = new[]
{
"4/032/004/2",
"4/032/004/9",
"4/032/004/3",
"4/032/004/3/A",
"4/032/004/3/B",
"4/032/004/11"
};
var ordered = licenceNumbers
.Select(n => n.Split(new[] { '/' }))
.OrderBy(t => t, new LicenceNumberComparer())
.Select(t => String.Join("/", t));
Using the following comparer:
public class LicenceNumberComparer: IComparer<string[]>
{
public int Compare(string[] a, string[] b)
{
var len = Math.Min(a.Length, b.Length);
for(var i = 0; i < len; i++)
{
var aIsNum = int.TryParse(a[i], out int aNum);
var bIsNum = int.TryParse(b[i], out int bNum);
if (aIsNum && bIsNum)
{
if (aNum != bNum)
{
return aNum - bNum;
}
}
else
{
var strCompare = String.Compare(a[i], b[i]);
if (strCompare != 0)
{
return strCompare;
}
}
}
return a.Length - b.Length;
}
}
If we can assume that
Number plate constist of several (one or more) parts separated by '/', e.g. 4, 032, 004, 2
Each part is not longer than some constant value (3 in the code below)
Each part consist of either digits (e.g. 4, 032) or non-digits (e.g. A, B)
We can just PadLeft each number plate's digit part with 0 in order to compare not "3" and "11" (and get "3" > "11") but padded "003" < "011":
var source = new string[] {
"4/032/004/2",
"4/032/004/9",
"4/032/004/3/A",
"4/032/004/3/B",
"4/032/004/11",
};
var ordered = source
.OrderBy(item => string.Concat(item
.Split('/') // for each part
.Select(part => part.All(char.IsDigit) // we either
? part.PadLeft(3, '0') // Pad digit parts e.g. 3 -> 003, 11 -> 011
: part))); // ..or leave it as is
Console.WriteLine(string.Join(Environment.NewLine, ordered));
Outcome:
4/032/004/2
4/032/004/3/A
4/032/004/3/B
4/032/004/9
4/032/004/11
You seem to be wanting to sort on the fourth element of the string (delimited by /) in numeric rather than string mode.. ?
You can make a lambda more involved/multi-statement by putting it like any other method code block, in { }
var orderedData = allLicenceNumbers
.OrderBy(x =>
{
var t = x.Split('/');
if(t.Length<4)
return -1;
else{
int o = -1;
int.TryParse(t[3], out o);
return o;
}
)
.ToList();
If you're after sorting on more elements of the string, you might want to look at some alternative logic, perhaps if the first part of the string will always be in the form N/NNN/NNN/??/?, then do:
var orderedData = allLicenceNumbers
.OrderBy(w => w.Remove(9)) //the first 9 are always in the form N/NNN/NNN
.ThenBy(x => //then there's maybe a number that should be parsed
{
var t = x.Split('/');
if(t.Length<4)
return -1;
else{
int o = -1;
int.TryParse(t[3], out o);
return o;
}
)
.ThenBy(y => y.Substring(y.LastIndexOf('/'))) //then there's maybe A or B..
.ToList();
Ultimately, it seems that more and more outliers will be thrown into the mix, so you're just going to have to keep inventing rules to sort with..
Either that or change your strings to standardize everything (int an NNN/NNN/NNN/NNN/NNA format for example), and then sort as strings..
var orderedData = allLicenceNumbers
.OrderBy(x =>
{
var t = x.Split('/');
for(int i = 0; i < t.Length; i++) //make all elements in the form NNN
{
t[i] = "000" + t[i];
t[i] = t[i].Substring(t[i].Length - 3);
}
return string.Join(t, "/");
}
)
.ToList();
Mmm.. nasty!

String manipulation in alternating order

I have a string
string value = "123456789";
now I need to re-arrange the string in the following way:
123456789
1 right
12 left
312 right
3124 left
53124 right
...
975312468 result
Is there a fancy linq one liner solution to solve this?
My current (working but not so good looking) solution:
string items = "abcdefgij";
string result = string.Empty;
for (int i = 0; i < items.Length; i++)
{
if (i % 2 != 0)
{
result = result + items[i];
}
else
{
result = items[i] + result;
}
}
string value = "123456789";
bool b = true;
string result = value.Aggregate(string.Empty, (s, c) =>
{
b = !b;
return b ? (s + c) : (c + s);
});
I actually don't like local variables inside LINQ statements, but in this case b helps alternating the direction. (#klappvisor showed how to live without b).
You can use length of the res as variable to decide from which side to append
items.Aggregate(string.Empty, (res, c) => res.Length % 2 == 0 ? c + res : res + c);
Alternative solution would be zipping with range
items.Zip(Enumerable.Range(0, items.Length), (c, i) => new {C = c, I = i})
.Aggregate(string.Empty, (res, x) => x.I % 2 == 0 ? x.C + res : res + x.C)
EDIT: don't really needed ToCharArray...
Resulting string is chars in evens positions concatenated to chars in odds positions in reverse order:
string value = "123456789";
var evens = value.Where((c, i) => i % 2 == 1);
var odds = value.Where((c, i) => i % 2 == 0).Reverse();
var chars = odds.Concat(evens).ToArray();
var result = new string(chars);

how to arrange the item of a list into series arrangement

I have a list of data which contains of random data with combination of string and number:
List<String> Data1 = new List<String>()
{
"1001A",
"1002A",
"1003A",
"1004A",
"1015A",
"1016A",
"1007A",
"1008A",
"1009A",
};
I want this data to arrange into series like this:
1001A - 1004A, 1007A - 1009A, 1015A, 1016A
for every more than 2 counts of data series the output shall be have "-" between the first count and the last count of series, the other non series data will be just added to the last part and all together will separated by ",".
I'd already made some codes only to arrange the data series by the last char of it:
string get_REVISIONMARK = "A";
var raw_serries = arrange_REVISIONSERIES.Where(p => p[p.Length - 1].ToString() == get_REVISIONMARK) .OrderBy(p => p[p.Length - 1) .ThenBy(p => p.Substring(0, p.Length - 1)).ToList();
just ignore the last char I'd already have function for that, and my problem only about the arrangement of the numbers, the length of data is not fixed. for other example of output "1001A - 1005A, 301A, 32A"
I had another sample of my codes this works fine to me, but for me its so lazy code.
for (int c1 = 0; c1 < list_num.Count; c1++)
{
if (list_num[c1] != 0)
{
check1 = list_num[c1];
for (int c2 = 0; c2 < list_num.Count; c2++)
{
if (check1 == list_num[c2])
{
list_num[c2] = 0;
check1 += 1;
list_series.Add(arrange_REVISIONSERIES[c2]);
}
}
check1 = 0;
if (list_series.Count > 2)
{
res_series.Add(list_series[0] + " to " +list_series[list_series.Count - 1]);
list_series.Clear();
}
else
{
if (list_series.Count == 1)
{
res_series.Add(list_series[0]);
list_series.Clear();
}
else
{
res_series.Add(list_series[0] + "," + list_series[1]);
list_series.Clear();
}
}
}
}
var combine_res = String.Join(",", res_series);
MessageBox.Show(combine_res);
this codes work fine for the series number ...
A possible solution (working with current set of values), Please follow the steps below
Declare a class level string list as
public List<String> data_result = new List<string>();
Create a function to iterate through input string list (input string declared inside, named 'data')
public void ArrangeList()
{
List<String> data = new List<string>() { "1001A", "1002A", "1003A",
"1004A", "1015A", "1016A", "1007A", "1008A", "1009A", "1017A" };
List<int> data_int = data.Select(a => Convert.ToInt32(a.Substring(0,
a.Length - 1))).OrderBy(b => b).ToList();
int initializer = 0, counter = 0;
int finalizer = 0;
foreach (var item in data_int)
{
if (initializer == 0)
{ initializer = item; continue; }
else
{
counter++;
if (item == initializer + counter)
finalizer = item;
else
{
LogListing(initializer, finalizer);
initializer = item;
finalizer = item;
counter = 0;
}
}
}
LogListing(initializer, finalizer);
}
Create a function which just logs the result into data_result string list.
public void LogListing(int initializer, int finalizer)
{
if (initializer != finalizer)
{
if (finalizer == initializer + 1)
{
data_result.Add(initializer + "A");
data_result.Add(finalizer + "A");
}
else
data_result.Add(initializer + "A - " + finalizer + "A");
}
else
data_result.Add(initializer + "A");
}
It perfectly generates the result list as
Thumb-up if you like
A linqy solution:
char get_REVISIONMARK = 'A';
var res = arrange_REVISIONSERIES.Select(s => new { Rev = s[s.Length - 1], Value = int.Parse(s.Substring(0, s.Length - 1)), Org = s })
.Where(d => d.Rev == get_REVISIONMARK).OrderBy(d => d.Value)
.Select((val, ind) => new { Index = ind, Org = val.Org, Value = val.Value }).GroupBy(a => a.Value - a.Index)
.Select(gr=>gr.ToList()).OrderBy(l=>l.Count > 2 ? 0 : 1 ).Aggregate(new List<string>(), (list, sublist) =>
{
if (sublist.Count > 2)
list.Add(sublist[0].Org + " - " + sublist[sublist.Count - 1].Org);
else
list.AddRange(sublist.Select(a => a.Org));
return list;
});
The first lines are basically the same as the code you already have (filter on revision and sort), but with the difference that the subvalues are stored in an anonymous type. You could do the same on the pre ordered list, but since splitting the string would be done twice I've included it in the total.
Then a select with index (.Select((val, ind) =>) is made to get value/index pairs. This is done to be able to get the sequences based on an old t-sql row_number trick: for each 'group' the difference between value and index is the same .GroupBy(a => a.Value - a.Index)
After that, normally you'd be as good as done, but since you only want to make sequences of 2 and longer, we make sublists out of the groupby values and do the ordering beforehand to make sure the ranges come for the eventual single elements .Select(gr=>gr.ToList()).OrderBy(l=>l.Count > 2 ? 0 : 1 )
Finally, the list is created of the groups. Several options, but I like to use Aggregate for that. The seed is the resulting list, and the aggregate simply adds to that (where subranges > 2 are cummulated and for single elements and pairs, the single elements are added)
I'm making two assumptions:
The list is already ordered
The non-numeric characters can be ignored
You will get the results in the results variable:
void Main()
{
List<String> Data1 = new List<String>()
{
"1001A",
"1002A",
"1003A",
"1004A",
"1015A",
"1016A",
"1007A",
"1008A",
"1009A",
};
var accu = new List<List<Tuple<int, string>>>();
foreach (var data in Data1)
{
if (accu.Any(t => t.Any(d => d.Item1 == (ToInt(data) - 1))))
{
var item = accu.First(t => t.Any(d => d.Item1 == (ToInt(data) - 1)));
item.Add(new Tuple<int, string>(ToInt(data), data));
}
else
{
accu.Add(new List<Tuple<int, string>>{ new Tuple <int, string>(ToInt(data), data)});
}
}
var results = new List<string>();
results.AddRange(accu.Where(g => g.Count > 2).Select(g => string.Format("{0} - {1}", g.First().Item2, g.Last().Item2)));
results.AddRange(accu.Where(g => g.Count <= 2).Aggregate(new List<string>(), (total, current) => { total.AddRange(current.Select(i => i.Item2)); return total; } ));
}
private static Regex digitsOnly = new Regex(#"[^\d]");
public static int ToInt(string literal)
{
int i;
int.TryParse(digitsOnly.Replace(literal, ""), out i);
return i;
}
So given your starting data:
List<String> arrange_REVISIONSERIES = new List<String>()
{
"1001A",
"1002A",
"1003A",
"1004A",
"1015A",
"1016A",
"1007A",
"1008A",
"1009A",
};
I do this first:
var splits =
arrange_REVISIONSERIES
.Select(datum => new
{
value = int.Parse(datum.Substring(0, datum.Length - 1)),
suffix = datum.Substring(datum.Length - 1, 1),
})
.OrderBy(split => split.suffix)
.ThenBy(split => split.value)
.ToArray();
That's basically the same as your raw_serries, but orders the number part as a number. It seems to me that you need it as a number to make the range part work.
I then do this to compute the groupings:
var results =
splits
.Skip(1)
.Aggregate(
new[]
{
new
{
start = splits[0].value,
end = splits[0].value,
suffix = splits[0].suffix
}
}.ToList(),
(a, s) =>
{
if (a.Last().suffix == s.suffix && a.Last().end + 1 == s.value)
{
a[a.Count - 1] = new
{
start = a.Last().start,
end = s.value,
suffix = s.suffix
};
}
else
{
a.Add(new
{
start = s.value,
end = s.value,
suffix = s.suffix
});
}
return a;
})
.Select(r => r.start == r.end
? String.Format("{0}{1}", r.end, r.suffix)
: (r.start + 1 == r.end
? String.Format("{0}{2}, {1}{2}", r.start, r.end, r.suffix)
: String.Format("{0}{2} - {1}{2}", r.start, r.end, r.suffix)))
.ToArray();
And finally, this to create a single string:
var result = String.Join(", ", results);
That gives me:
1001A - 1004A, 1007A - 1009A, 1015A, 1016A
This code nicely works with data containing different suffixes.

How to find maximum number of repeated string in a string in a list of string in c#

If we have a list of strings, then how we can find the list of strings that have the maximum number of repeated symbol by using LINQ.
List <string> mylist=new List <string>();
mylist.Add("%1");
mylist.Add("%136%250%3"); //s0
mylist.Add("%1%5%20%1%10%50%8%3"); // s1
mylist.Add("%4%255%20%1%14%50%8%4"); // s2
string symbol="%";
List <string> List_has_MAX_num_of_symbol= mylist.OrderByDescending(s => s.Length ==max_num_of(symbol)).ToList();
//the result should be a list of s1 + s2 since they have **8** repeated '%'
I tried
var longest = mylist.Where(s => s.Length == mylist.Max(m => m.Length)) ;
this gives me only one string not both
Here's a very simple solution, but not exactly efficient. Every element has the Count operation performed twice...
List<string> mylist = new List<string>();
mylist.Add("%1");
mylist.Add("%136%250%3"); //s0
mylist.Add("%1%5%20%1%10%50%8%3"); // s1
mylist.Add("%4%255%20%1%14%50%8%4"); // s2
char symbol = '%';
var maxRepeat = mylist.Max(item => item.Count(c => c == symbol));
var longest = mylist.Where(item => item.Count(c => c == symbol) == maxRepeat);
It will return 2 strings:
"%1%5%20%1%10%50%8%3"
"%4%255%20%1%14%50%8%4"
Here is an implementation that depends upon SortedDictionary<,> to get what you're after.
var mylist = new List<string> {"%1", "%136%250%3", "%1%5%20%1%10%50%8%3", "%4%255%20%1%14%50%8%4"};
var mappedValues = new SortedDictionary<int, IList<string>>();
mylist.ForEach(str =>
{
var count = str.Count(c => c == '%');
if (mappedValues.ContainsKey(count))
{
mappedValues[count].Add(str);
}
else
{
mappedValues[count] = new List<string> { str };
}
});
// output to validate output
foreach (var str in mappedValues.Last().Value)
{
Console.WriteLine(str);
}
Here's one using LINQ that gets the result you're after.
var result = (from str in mylist
group str by str.Count(c => c == '%')
into g
let max = (from gKey in g select g.Key).Max()
select new
{
Count = max,
List = (from str2 in g select str2)
}).LastOrDefault();
OK, here's my answer:
char symbol = '%';
var recs = mylist.Select(s => new { Str = s, Count = s.Count(c => c == symbol) });
var maxCount = recs.Max(x => x.Count);
var longest = recs.Where(x => x.Count == maxCount).Select(x => x.Str).ToList();
It is complicated because it has three lines (the char symbol = '%'; line excluded), but it counts each string only once. EZI's answer has only two lines, but it is complicated because it counts each string twice. If you really want a one-liner, here it is:
var longest = mylist.Where(x => x.Count(c => c == symbol) == mylist.Max(y => y.Count(c => c == symbol))).ToList();
but it counts each string many times. You can choose whatever complexity you want.
We can't assume that the % is always going to be the most repeated character in your list. First, we have to determine what character appears the most in an individual string for each string.
Once we have the character and it maximum occurrence, we can apply Linq to the List<string> and grab the strings that contain the character equal to its max occurrence.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
List <string> mylist=new List <string>();
mylist.Add("%1");
mylist.Add("%136%250%3");
mylist.Add("%1%5%20%1%10%50%8%3");
mylist.Add("%4%255%20%1%14%50%8%4");
// Determine what character appears most in a single string in the list
char maxCharacter = ' ';
int maxCount = 0;
foreach (string item in mylist)
{
// Get the max occurrence of each character
int max = item.Max(m => item.Count(c => c == m));
if (max > maxCount)
{
maxCount = max;
// Store the character whose occurrence equals the max
maxCharacter = item.Select(c => c).Where(c => item.Count(i => i == c) == max).First();
}
}
// Print the strings containing the max character
mylist.Where(item => item.Count(c => c == maxCharacter) == maxCount)
.ToList().ForEach(Console.WriteLine);
}
}
Results:
%1%5%20%1%10%50%8%3
%4%255%20%1%14%50%8%4
Fiddle Demo
var newList = myList.maxBy(x=>x.Count(y=>y.Equals('%'))).ToList();
This should work. Please correct syntax if wrong anywhere and update here too if it works for you.

Rename the Repeated Values in array c#

I have a array which looks like this :-
a[53]={ARPNUM-T,
OR1PATTYP-T,
IVNUM-T,
IVDESC-T,
ORDEPT-T,
ARPNAME-T,
ARGNAME-T,
ARPATADDR1-T,
ARPATCITY-T,
ARPATSTATE-T,
ARPATZIP-N,
ARSEX-T,
ARBIRTH-N,
ARSSN-T,
ARMARRY-T,
ARADMDT-N,
ARDISDT-N,
ARPEMP-T,
ARPHY1-T,
ARPHYNM1-T,
ARMRNUM-T,
ARGUARSSN-T,
ARPHONE-T,
AREMPLYR-T,
ARADDR1-T,
ARSTATE-T,
ARZIP-N,
ARPATPHONE-N,
ARDIAG01-T,
ISSUBNAME-T,
ISCOMPCD-T,
ISCONAME-T,
ISCONTRAC-T,
ISGROUP-T,
ISPRIMARY-T,
ISCOADDR-N,
ISCOCITST-T,
ISPATREL-T,
ISCERTNO-T,
ISCOZIP-N,
ISSUBNAME-T,
ISCOMPCD-T,
ISCONAME-T,
ISCONTRAC-T,
ISGROUP-T,
ISPRIMARY-T,
ISCOADDR-N,
ISCOCITST-T,
ISPATREL-T,
ISCERTNO-T,
ISCOZIP-N,
ARCITY-T}
There are some repeated values like ISSUBNAME-T,ISCOMPCD-T.
i need to fill the array a to array b
where repeated value will be suffixed by the number of times of repetition ,
For eg - if ISSUBNAME-T is repeated 3 times then ISSUBNAME-T_3 .
I have tried a code:-
for (int d = 1; d < 53; d++)
{
b[0] = a[0];
for (int k = 1; k < d; k++)
{
int count = 0;
//b[d] = a[d];
if (a[d] == a[d - k])
{
count++;
if (count > 0)
{
b[d] = a[d] + "_" + count + "";
}
else
{
b[d] = a[d];
}
//Console.WriteLine(count);
}
//Console.WriteLine(count);
}
//Console.WriteLine(count);
}
But it's not showing correct output.
Group array items by their values. Then check if group contains more than one item. If so, then return formatted item value, otherwise simply return item value:
string[] b = a.GroupBy(i => i)
.Select(g => g.Count() > 1 ?
String.Format("{0}_{1}", g.Key, g.Count()) : g.Key)
.ToArray();
Query syntax (easily allows to calculate group length only once):
var query = from i in a
group i by i into g
let count = g.Count()
select count > 1 ? String.Format("{0}_{1}", g.Key,count) : g.Key;
string[] b = query.ToArray();
UPDATE: If you want to keep all items and have incremental suffixes
string[] b = a.GroupBy(e => e)
.SelectMany(g => g.Count() == 1 ?
g.Take(1) :
g.Select((e,i) => String.Format("{0}_{1}", e,i+1))
.ToArray();
UPDATE 2: If you want also preserving original order, then simple loop and dictionary will be simpler
string[] b = new string[a.Length];
var duplicatedItems = a.GroupBy(a => a)
.Where(g => g.Count() > 0)
.ToDictionary(g => g.Key, g => g.Count());
for(int i = b.Length - 1; i >= 0 ; i--)
{
string item = a[i];
if (!duplicatedItems.ContainsKey(item))
{
b[i] = item;
continue;
}
b[i] = String.Format("{0}_{1}", item, duplicatedItems[item]);
duplicatedItems[item]--;
}
Linq query for comparison
string[] b =
a.Select((e,i) => new { Item = e, Index = i })
.GroupBy(x => x.Item)
.SelectMany(g => g.Count() == 1 ?
g.Take(1) :
g.Select((x,i) => new {
Item = String.Format("{0}_{1}", x.Item, i+1),
Index = x.Index
}))
.OrderBy(x => x.Index)
.Select(x => x.Item)
.ToArray();

Categories

Resources