Sorting a SortedDictionary of <String,int> Alphabetically, reverse Alphabetically, and the value's frequency

StackOverflow https://stackoverflow.com/questions/13076556

Question

I'm brand new to C# and the concept of hash tables/dictionary's and have been searching for a straight answer on this for quite awhile, but I can not find any solutions that actually works (likely due to my ignorance of the subject) so I need a solution in simple terms if possible.

Also, if it makes a difference, the Strings and ints in the dictionary are not data members or even parameters.

More Description:

The program overall, is to import words via text file or direct user input then save them and the number of times they appear (AKA store in Dictionary/Sorteddictionary) then allow users to reorganize the data, alphabetically, reverse alphabetically, or by frequency and/OR print them to the console or write the data to a new .txt file.

However right now I am only trying to get the sorting of the dictionary to work.

Example input: Another great story and another great adventure.

Example Output(alphabetically): Words Starting with letter A: another adventure and Letter Starting with letter G: great (Ect.)

Example Output(reverse alphabetically): Words Starting with letter S: story Words Starting with letter G: great (ect.)

Output(frequency): number of words occurring 2 time(s): another great number of words occurring 1 time(s): and adventure story.

Was it helpful?

Solution

Hope this helps. I'm not sure it's the best way to approach your problem, but it should help you to familiarise yourself with some of the options / see why some of the guys on here are advising against it. If you let us know more about what you're trying to do we can better advise alternate approaches.

using System;
using System.Collections.Generic;
using System.Linq;

namespace StackOverflow.Demos
{
    class Program
    {

        const string OutputFormat = "{0}: {1}";
        public static void Main(string[] args)
        {
            new Program();
            Console.WriteLine("Done");
            Console.ReadKey();
        }
        public Program()
        {
            SortedDictionary<string, int> dic = new SortedDictionary<string, int>();
            dic.Add("a", 1);
            dic.Add("b", 2);
            dic.Add("d", 2);
            dic.Add("c", 1);
            dic.Add("e", 1);
            dic.Add("f", 3);
            dic.Add("g", 4);
            dic.Add("h", 2);
            dic.Add("i", 2);

            OutputByKeyAsc(dic);
            OutputByKeyDesc(dic);
            OutputByValueFrequency(dic);
        }

        void OutputByKeyAsc(SortedDictionary<string, int> dic)
        {
            Console.WriteLine("OutputByKeyAsc");
            foreach (string key in dic.Keys)
            {
                Console.WriteLine(string.Format(OutputFormat, key, dic[key]));
            }
        }

        void OutputByKeyDesc(SortedDictionary<string, int> dic)
        {
            Console.WriteLine("OutputByKeyDesc");
            foreach (string key in dic.Keys.Reverse())
            {
                Console.WriteLine(string.Format(OutputFormat, key, dic[key]));
            }
        }

        void OutputByValueFrequency(SortedDictionary<string, int> dic)
        {
            Console.WriteLine("OutputByValueFrequency");

            IEnumerable<KeyValuePair<int,int>> values = 
                (
                    from sortedItem 
                    in 
                    (
                        from entry 
                        in dic 
                        group entry 
                        by entry.Value
                        into result
                        select new KeyValuePair<int,int>(result.Key , result.Count())
                    )
                    orderby sortedItem.Value descending
                    select sortedItem
                ).ToArray();

            foreach (KeyValuePair<int, int> value in values)
            {
                foreach (KeyValuePair<string, int> item in dic.Where<KeyValuePair<string, int>>(item => item.Value == value.Key))
                {
                    Console.WriteLine(string.Format(OutputFormat, item.Key, string.Format(OutputFormat, item.Value, value.Value)));
                }
            }
        }

    }
}

Good luck / hope you're enjoying c# so far.

EDIT

Based on the new info in your question here's my attempt at a neater solution:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace StackOverflow.Demos
{
    class Program
    {

        const string OutputFormat = "{0}: {1}";
        public static void Main(string[] args)
        {
            new Program("Another great story and another great adventure.");
            Console.WriteLine("Done");
            Console.ReadKey();
        }
        public Program(string userInput)
        {
            //break string into words
            IEnumerable<IGrouping<string, int>> words = Regex.Split(userInput, @"\W+").GroupBy(word => word.ToLowerInvariant(), word => 1); //nb converting word to lower case to avoid case sensitive comparisons in grouping - I can keep the original value(s) by replacing "word => 1" with "word => word" if needed

            Console.WriteLine("\nWords in alphabetic order");
            foreach (IGrouping<string, int> wordInfo in words.OrderBy(word => word.Key))
            {
                Console.WriteLine(string.Format(OutputFormat, wordInfo.Key,wordInfo.Count()));
            }

            Console.WriteLine("\nWords in descending alphabetic order");
            foreach (IGrouping<string, int> wordInfo in words.OrderByDescending(word => word.Key))
            {
                Console.WriteLine(string.Format(OutputFormat, wordInfo.Key, wordInfo.Count()));
            }

            Console.WriteLine("\nWords by frequency (desc)");
            foreach (IGrouping<string, int> wordInfo in words.OrderByDescending(word => word.Count()))
            {
                Console.WriteLine(string.Format(OutputFormat, wordInfo.Key, wordInfo.Count()));
            }
        }
    }
}

EDIT

Here's the same code with the functionality in a class and the output kept in the program: using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions;

namespace StackOverflow.Demos
{
    class Program
    {

        const string OutputFormat = "{0}: {1}";
        public static void Main(string[] args)
        {
            new Program("Another great story and another great adventure.");
            Console.WriteLine("Done");
            Console.ReadKey();
        }
        public Program(string userInput)
        {
            WordCounter myWordCounter = new WordCounter(userInput);
            Console.WriteLine("\n**Alphabetical**");
            foreach (KeyValuePair<string, int> wordInfo in myWordCounter.GetWordCountByWordAlphabeticalDesc())
            {
                Console.WriteLine(string.Format(OutputFormat,wordInfo.Key, wordInfo.Value));
            }
            Console.WriteLine("\n**Alphabetical Desc**");
            foreach (KeyValuePair<string, int> wordInfo in myWordCounter.GetWordCountByWordAlphabeticalDesc())
            {
                Console.WriteLine(string.Format(OutputFormat, wordInfo.Key, wordInfo.Value));
            }
            Console.WriteLine("\n**Frequency Desc**");
            foreach (KeyValuePair<string, int> wordInfo in myWordCounter.GetWordCountByFrequency())
            {
                Console.WriteLine(string.Format(OutputFormat, wordInfo.Key, wordInfo.Value));
            }
        }

    }

    public class WordCounter
    {
        string sentance;
        IEnumerable<IGrouping<string, int>> words;
        public WordCounter(string sentance)
        {
            this.sentance = sentance;
            GetWords();
        }
        void GetWords()
        {
            this.words = Regex.Split(this.sentance, @"\W+").GroupBy(word => word.ToLowerInvariant(), word => 1);
        }
        public IEnumerable<KeyValuePair<string, int>> GetWordCountByWordAlphabetical()
        {
            return this.words.OrderBy(word => word.Key).Select(wordInfo => new KeyValuePair<string,int>(wordInfo.Key, wordInfo.Count()));
        }
        public IEnumerable<KeyValuePair<string, int>> GetWordCountByWordAlphabeticalDesc()
        {
            return this.words.OrderByDescending(word => word.Key).Select(wordInfo => new KeyValuePair<string, int>(wordInfo.Key, wordInfo.Count()));
        }
        public IEnumerable<KeyValuePair<string, int>> GetWordCountByFrequency()
        {
            return this.words.OrderByDescending(word => word.Count()).Select(wordInfo => new KeyValuePair<string, int>(wordInfo.Key, wordInfo.Count()));
        }
    }
}

OTHER TIPS

Since you aren't always displaying the data in a single consistently sorted manor I don't see any compelling reason to store the data in a sorted manor. You're likely better off just storing the data however you want, and then sorting it whenever the user requests to see it in some particularly sorted manor.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top