Frankish EQG
Frankish EQG

Reputation: 114

Saving Dictionary contents to file

I'm trying to do something pretty specific with lists and counting unique instances, then saving that on a new line in a file.

Essentially, I have a listview with a set of columns, I want to loop through the whole list, and save a count of each unique item.

Example, a list like so;

111
222
111
333
333
333

would end up being written to file as:

111:2
222:1
333:3

I dont need it to be in any particular order, just so long as I have those values.

So far I have this;

string fileName = Application.StartupPath + @"\txn_" + 
    TerminalConfig.CreateSafSig() + ".saf";

Dictionary<string, int> products = new Dictionary<string, int>();
List<string> codes = new List<string>();

foreach (ListViewItem item in BasketList.Items)
{ 
    codes.Add(item.SubItems[3].Text); 
}

String[] items = codes.ToArray();
foreach (String code in items)
{
    if (products.ContainsKey(code) != true)
    { 
        products.Add(code, 1); 
    }
    else 
    { 
        products[code] += 1; 
    }
}

foreach (var entry in products)
{ 
    File.WriteAllText(fileName, string.Format("{0}:{1}",entry.Key,entry.Value)); 
}

But the file it saves is only giving me what would be the last line. in the example above, it would only show 333:3

I was pretty sure I wrote it correctly, and I'm struggling to find where I've gone wrong.

Upvotes: 1

Views: 429

Answers (4)

Cory Nelson
Cory Nelson

Reputation: 29981

Harrison answered why your code is not working... now let me show you why Jonesy (rudely) suggested you use GroupBy...

File.WriteAllLines(fileName,
               from item in BasketList.Items
               group item by item.SubItems[3].Text into grp
               select string.Format("{0}:{1}", grp.Key, grp.Count()));

This effectively replaces all of your code. It is less efficient -- GroupBy creates groups of the items when really you only need a count, so it is a bit heavyweight on memory usage -- but that isn't often a hugely important factor. There is something to be said about being succinct.

Upvotes: 2

Hogan
Hogan

Reputation: 70523

The problem is you overwrite the file with every iteration of the loop.

Code fix:

StringBuilder str = new StringBuilder();


foreach (var entry in products)
{
   str.AppendLine(string.Format("{0}:{1}", entry.Key, entry.Value));
}
File.WriteAllText(fileName, str.ToString()); }

To jump on the linq bandwagon here is the code to make a dictionary:

Dictionary<string, int> products = 
    BasketList.Items.GroupBy(element => element.SubItems[3].Text)
                    .ToDictionary(k => k.Key, c => c.Count())

Yes in linq that replaces all those lines in one.

Upvotes: 2

Harrison
Harrison

Reputation: 3953

File.WriteAllText writes a new file. You are overwriting the file each time you iterate through your for loop giving you only the last line.

The msdn page shows

Creates a new file, write the contents to the file, and then closes the file. If the target file already exists, it is overwritten.

You could replace the File.WriteAllText with File.AppendAllText which:

Opens a file, appends the specified string to the file, and then closes the file. If the file does not exist, this method creates a file, writes the specified string to the file, then closes the file.

If you want to write the file all at once, you could use File.WriteAllLines(string path,IEnumerable<string> contents); which

Creates a new file, writes a collection of strings to the file, and then closes the file.

In your case replace :

foreach (var entry in products)
{ 
    File.WriteAllText(fileName, string.Format("{0}:{1}",entry.Key,entry.Value)); 
}

with

var entries = from entry in products select string.Format("{0}:{1}",entry.Key,entry.Value);
File.WriteAllLines(fileName,entries);

Upvotes: 4

Golden Dragon
Golden Dragon

Reputation: 511

The WriteAllText function will overwrite the file contents with the string you supply. You should use the AppendAllText function instead

Upvotes: 0

Related Questions