Adding and Couting items in a list
Let's say I have a file that looks like this:
R34 128590 -74.498 109.728 0 0805_7
R33 128590 -74.498 112.014 0 0805_7
R15 128588 -68.910 127.254 0 0805_7
R32 128587 -65.354 115.189 0 0805_7
R35 128587 -65.354 117.348 0 0805_7
R38 128590 -65.354 119.507 0 0805_7
What I want to do is add the 2nd column to a list and have a counter count how many times that item occurs and outputs it with the number and then the counted amount of that number.
Is there a way to do this using a List? If so, how could I go about doing that?
I have tried messing around with things and this is where I was heading.. but it does not work properly
int lineCount = 1;
int itemCounter = 0;
foreach (var item in aListBox.Items)
{
// Creates a string of the items in the ListBox.
var newItems = item.ToString();
// Replaces any multiple spaces (tabs) with a single space.
newItems = Regex.Replace(newItems, @"\s+", " ");
// Splits each line by spaces.
var eachItem = newItems.Split(' ');
###
### HERE is where I need help ###
###
List<string> partList = new List&开发者_如何学Clt;string>();
partList.Add(eachItem[1]);
if (partList.Contains(eachItem[1]))
itemCounter++;
else
partList.Add(eachItem[1]);
sw.WriteLine(lineCount + ": "+ partList + ": " + itemCounter);
lineCount++;
}
SO for the example above, it would output this:
1: 128590: 3 #lineCount, partList, itemCounter
2: 128588: 1
3: 128587: 2
Can someone help me figuring out how to properly do this?
use linq with count and group by (see Count- Grouped section).
create your partList
outside the foreach loop and add each item to it inside the loop , so that it would contain all of the elements:
List<string> partList = new List<string>();
foreach (var item in aListBox.Items)
{
//regex stuff here...
partList.Add(eachItem[1]);
}
(in your example- {128590, 128590, 128588, 128587, 128587, 128590}
)
and then use LINQ to output the result-
var elementsWithCounts = from p in partList
group p by p into g
select new { Item = g.Key, Count = g.Count()};
I would either use a Linq query or a Dictionary
something like
List<string> items = new List<string>{"128590", "128590", "128588", "128587", "128587", "128590"};
Dictionary<string,int> result = new Dictionary<string,int>();
foreach( int item in items )
{
if(result.ContainsKey(item) )
result[item]++;
else
result.Add(item,1);
}
foreach( var item in result )
Console.Out.WriteLine( item.Key + ":" + item.Value );
Once you have the items split by space, I'm assuming you have a string array looking like so:
[0] = "R34"
[1] = "128590"
[2] = "-74.498"
[3] = "109.728"
[4] = "0"
[5] = "0805_7"
You can simply perform this operation with a Group By operation.
var items = aListBox.Items.Select(x => /* Split Code Here and Take Element 1 */).GroupBy(x => x);
foreach(var set in items)
{
Console.WriteLine(set.Key + " appeared " + set.Count() + " times.");
}
Basically, you are trying to do this by iterating once, and that is not really going to work, you are going to have to iterate twice, otherwise you will wind up doing an output every time you loop in the foreach, and even if your accurate you are going to be outputting a new line each time. If you need to really use a List instead of a keyed dictionary or hashtable which would be idea for this (key = number, value = count), then you need to build the list first, then summarize the list. You can either use LINQ Group By (which is a bit terse), or create a function that does something similar to what you already have. If you are trying to learn concepts, look at the code below, it could be more condensed but this should be fairly easy to read.
List<string> partList = new List<string>();
List<string> displayedNumbers = new List<int>();
// Build the original list first.
foreach (var item in aListBox.Items)
{
// Creates a string of the items in the ListBox.
var newItems = item.ToString();
// Replaces any multiple spaces (tabs) with a single space.
newItems = Regex.Replace(newItems, @"\s+", " ");
// Splits each line by spaces.
var eachItem = newItems.Split(' ');
partList.Add(eachItem[1]);
}
// Now run through that list and count how many times the same number occurs.
// You will need two loops for this since your list is a single dimension collection.
foreach(var number in partList)
{
var innerList = partList;
// set this to zero because we are going to find at least 1 duplicate.
var count = 0;
foreach(var additionalNumber in innerList)
{
if(additionalNumber == number)
{
// If we find anymore increase the count each time.
count += 1;
}
}
// Now we have the full count of duplicates of the outer number in the list.
// If it has NOT been displayed, display it.
if(!displayedNumbers.Contains(number))
{
sw.WriteLine(partList + ": " + count);
displayedNumbers.Add(number);
}
}
Use a hash table instead ofa list. You can save the key as 128590,... and the value the number of times it has occurred. Before you insert the new value check if it is already present in the hashtable by using the Contains operation and if it is increment the value.
I think the biggest problem is getting from raw lines of your text field to individual values. My guess is this is a tab-delimited file with a known constant number of columns, in which case you could use String.Split() to separate the sub-strings. Once you have the strings separated, you can count the instances of the proper column pretty easily with a little LINQ. Given a list or collection of your file's lines:
var histogram = myListOfLines
//Split each string along spaces or tabs, and discard any zero-length strings
//caused by multiple adjacent delimiters.
.Select(s=>s.Split(new[]{'\t',' '}, StringSplitOptions.RemoveEmptyEntries))
//Optional; turn the array of strings produced by Split() into an anonymous type
.Select(a=>new{Col1=a[0], Col2=a[1], Col3=a[2], Col4=a[3], Col5=a[4]})
//Group based on the values of the second column.
.GroupBy(x=>x.Col2)
//Then, out of the grouped collection, get the count for each unique value of Col2.
.Select(gx=>new{gx.Key, gx.Count()});
精彩评论