3

I've read lots of posts about sorting a 2D array but I still can't master it so I was wondering if anyone can offer me some advice...

I have an aray which lists letters and quantity (I'm doing a frequency anaysis on a piece of text). I've read this data into a rectangle array and need to order it by highest frequency first. Here's my code so far:

    //create 2D array to contain ascii code and quantities
    int[,] letterFrequency = new int[26, 2];

    //fill in 2D array with ascaii code and quantities
    while (asciiNo <= 90)
     {

       while ((encryptedText.Length - 1) > counter)
      {
                if (asciiNo == (int)encryptedText[index])
               {
                      letterCount++;
               }
                counter++;
                index++;
      }

    letterFrequency[(storeCount), (0)] = (char)(storeCount+66);
    letterFrequency[(storeCount), (1)] = letterCount;
    storeCount++;
    counter=0;
    index=0;
    letterCount = 0;
    asciiNo++;
    }
6
  • Are you using the latest version of C#? Commented Jan 14, 2012 at 23:57
  • Yeah, I'm using VS 2010? Commented Jan 14, 2012 at 23:59
  • Can you give an example (maybe smaller) of what a sorted 2D array looks like? Do you mean sorting each row separately, for example? Commented Jan 15, 2012 at 0:00
  • Looks like you are getting some great feedback so I will bow out. I asked the C# question to ensure that you can take advantage of Linq Lambda Expressions. Commented Jan 15, 2012 at 0:03
  • The array will contain two columns - one for Character and one for frequency of that character. So in overview, it stores 26 different letters with it's associated frequency. Commented Jan 15, 2012 at 0:03

7 Answers 7

17

You are using a 2D array to represent 2 separate vectors - the symbols and the counts. Instead, use 2 separate arrays. Array.Sort has an overload that takes 2 arrays, and sorts on one array, but applies the changes to both, achieving what you want.

This would also allow you to use a char[] for the characters rather than int[]:

char[] symbols = ...
int[] counts = ...
...load the data...
Array.Sort(counts, symbols);
// all done!

At this point, the counts have been ordered, and the symbols will still match index-by-index with the count they relate to.

Sign up to request clarification or add additional context in comments.

Comments

4

You can wrap letter-count pair in a struct and use linq methods to manipulate data:

struct LetterCount {
    public char Letter { get; set; }
    public int Count { get; set; }
}

Sorting by count will look like this:

List<LetterCount> counts = new List<LetterCount>();
//filling the counts
counts = counts.OrderBy(lc => lc.Count).ToList();

4 Comments

That's the same as using the generic KeyValuePair<TKey, TValue>, except you get to name it.
Yes, but naming gives more readability and making a struct now can prepare the code for new logic in the future.
Imo it just clutters code, but like I say that's my opinion. Just like C++ with it's typedefs. If you have a zillion types that all refer to the same type. Then it only gets confusing. Once again, my opinion.
In this case it's a matter of taste, I agree.
1
public static void Sort2DArray<T>(T[,] matrix)
{
    var numb = new T[matrix.GetLength(0) * matrix.GetLength(1)];

    int i = 0;
    foreach (var n in matrix)
    {
        numb[i] = n;
        i++;
    }
    Array.Sort(numb);

    int k = 0;
    for (i = 0; i < matrix.GetLength(0); i++)
    {
        for (int j = 0; j < matrix.GetLength(1); j++)
        {
            matrix[i, j] = numb[k];
            k++;
        }
    }
}

Comments

0

Alternative approach:

var counts = new Dictionary<char,int>();
foreach(char c in text) {
    int count;
    counts.TryGetValue(c, out count);
    counts[c] = count + 1;
}
var sorted = counts.OrderByDescending(kvp => kvp.Value).ToArray();
foreach(var pair in sorted) {
    Console.WriteLine("{0}: {1}", pair.Key, pair.Value);
}

(untested)

3 Comments

Better make use of a SortedDictionary<TKey, TValue> then. But your other solution is the best :P.
@Aidiakapi no; that sorts on the key; we want to sort on the value
Sorry, I meant to say SortedList<TKey, TValue> ;)
0

In this case I'd choose to make use of KeyValuePair<TKey, TValue> and instead use something like this:

//create 2D array to contain ascii code and quantities
KeyValuePair<char, int>[] letterFrequency = new KeyValuePair<char, int>[26];

//fill in 2D array with ascaii code and quantities
while (asciiNo <= 90)
 {

   while ((encryptedText.Length - 1) > counter)
  {
            if (asciiNo == (int)encryptedText[index])
           {
                  letterCount++;
           }
            counter++;
            index++;
  }

letterFrequency[storeCount] = new KeyValuePair<char, int>((char)(storeCount+66), letterCount);
storeCount++;
counter=0;
index=0;
letterCount = 0;
asciiNo++;
}

Then use Array.Sort:

Array.Sort(letterFrequency, (i1, i2) => i2.Value.CompareTo(i1.Value));

Comments

0

This'll sort a two dimension array, the bool specifies if it's sorted on the second dimension, but default it sorts on the first dimension.

void SortDoubleDimension<T>(T[,] array, bool bySecond = false)
{
    int length = array.GetLength(0);
    T[] dim1 = new T[length];
    T[] dim2 = new T[length];
    for (int i = 0; i < length; i++)
    {
        dim1[i] = array[i, 0];
        dim2[i] = array[i, 1];
    }
    if (bySecond) Array.Sort(dim2, dim1);
    else Array.Sort(dim1, dim2);
    for (int i = 0; i < length; i++)
    {
        array[i, 0] = dim1[i];
        array[i, 1] = dim2[i];
    }
}

Comments

0

Why are you storing the character? You can infer it from the array index and do not need to store it! Use a one-dimensional array instead.

string encryptedText = "Test".ToUpper();
int[] frequency = new int[26];
foreach (char ch in encryptedText) {
    int charCode = ch - 'A';
    frequency[charCode]++;
}
var query = frequency
    .Select((count, index) => new { Letter = (char)(index + 'A'), Count = count })
    .Where(f => f.Count != 0)
    .OrderByDescending(f => f.Count)
    .ThenBy(f => f.Letter);
foreach (var f in query) {
    Console.WriteLine("Frequency of {0} is {1}", f.Letter, f.Count);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.