Sortowanie danych jest bardzo ważnym narzędziem analitycznym, które pozwala szybko ocenić dynamikę wzrostu lub spadku, a także uszeregować dane tak, aby były przyjazne dla czytelnika. Macierz w obecnej wersji FastReport .NET umożliwia sortowanie tylko miar. Na przykład, budujesz raport, który wyświetla statystyki sprzedaży pracowników według roku. Macierz ma pogrupowane dane według roku i miesiąca. Potrzebujemy posortować je w ramach każdej grupy - rok. Standardowe narzędzia sortowania pozwolą Ci posortować nazwy pracowników, lata, miesiące, ale nie dane. Szczególnie jeśli chcemy posortować po konkretnej kolumnie.
Aby posortować według konkretnej kolumny skonstruowanej macierzy (np. dla konkretnego pracownika), będziesz musiał użyć skryptu raportu. Dwa sposoby sortowania skonstruowanej macierzy to przesuwanie wierszy lub komórek.
Na pierwsze rzut oka wygląda, że przeniesienie wierszy na raz będzie dobrym rozwiązaniem, ponieważ sortowanie oznacza zmianę kolejności wyświetlania całego wiersza, a nie konkretnej komórki. W rzeczywistości, będzie to najbardziej poprawna decyzja, ale nie zawsze.
Przyjrzymy się przypadkowi, w którym przenoszenie wierszy nie będzie działać. Jeśli twoja macierz ma grupy z podgrupami w wymiarach, wtedy będziesz miał problemy z przeniesieniem pierwszego wiersza w grupie. Ten wiersz ma nazwę grupy w pierwszej komórce. Kolejne wiersze z grupy mają pustą wartość w pierwszej komórce. Ponieważ możesz zmienić kolejność wyświetlania pierwszego wiersza w grupie podczas sortowania, błąd pojawi się, gdy na jego miejsce przyjdzie wiersz z pustym nagłówkiem grupy.
Aby uniknąć takich błędów, będziesz musiał posortować komórki w odpowiedniej kolumnie. Oznacza to, że najpierw sortujesz wybraną kolumnę, a następnie, używając zestawu indeksów komórek, sortujesz wszystkie inne kolumny w macierzy według tej kolumny. Oczywiście, ta metoda jest znacznie bardziej czasochłonna.
Przyjrzyjmy się obu przypadkom na przykładzie. Pierwszym z nich jest sortowanie wierszy macierzy poprzez usuwanie i wstawianie wierszy do wynikowej macierzy.
Przyjrzyjmy się macierzy źródłowej, którą musimy posortować:
Ten zrzut ekranu pokazuje prostą macierz, która nie posiada grup z podgrupami. Przenoszenie wierszy jest idealne w tym przypadku. W rzeczywistości najpierw usuniemy wymagane wiersze, a następnie wstawimy je we właściwej kolejności. Zostanie to zrobione za pomocą skryptu raportu.
Załóżmy, że chcemy posortować macierz według kolumny dla roku 2011. Musimy określić liczbę porządkową tej kolumny i uzyskać dane dla wszystkich jej komórek, z wyjątkiem wynikowej Razem.
Dodajmy teraz zdarzenie ModifyResult dla obiektu macierzy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
public class ReportScript { // the key of the pair is the value in the cell of the matrix, the value of the pair is the y-coordinate of the cell // the dictionary will be sorted by cell value in this way private SortedDictionary<double, int> numbers = new SortedDictionary<double, int>(); private void Matrix1_ModifyResult(object sender, EventArgs e) { int x = 1; int y = 2; // let's collect the values of the cells in the column for 2011, we will sort by it for ( ; y < Matrix1.ResultTable.RowCount - 1; y++) { object val = Matrix1.ResultTable.GetCellData(x, y).Value; double dval = 0.0; if (val != null) { // here it is important to know the types of values or to check them // the cell format is Currency in this example, so we first convert it to a string // the default cell format is string Double.TryParse(val.ToString(), out dval); numbers.Add(dval, y); } // we add a pair with a value of 0.0 to the dictionary if there is an empty string in the cell // as a result, empty strings will be taken into account when sorting and will go first else { numbers.Add(dval, y); } } // copy the rows of the matrix into the backing array // then we will take the necessary rows from it and insert into the matrix object[] originalRows = Matrix1.ResultTable.Rows.ToArray(); int i = 2; // the number of the row where we will start deleting rows in the matrix // now we delete the second row in the matrix as many times as there are rows to be sorted // we keep deleting the second row, because all rows will move up one position after deleting it for (int j = 0; j < numbers.Count; j++) { Matrix1.ResultTable.Rows.RemoveAt(i); } i = 2; // now we just add all the rows in order according to the sorted list foreach (int v in numbers.Values) { int rowNum = v; Matrix1.ResultTable.Rows.Insert(i, originalRows[rowNum] as TableRow); i++; } } } |
W rzeczywistości idea metody polega na odczytaniu wartości komórek i ich indeksów z żądanej kolumny i zapisaniu ich do posortowanego słownika. Możemy ułożyć wiersze w żądanej kolejności za pomocą indeksów komórek i odpowiednio wierszy. W tym celu najpierw kopiujemy wiersze do listy tymczasowej. Następnie usuwamy wszystkie wiersze i wstawiamy je zgodnie z indeksami w posortowanym słowniku komórek. Do wstawienia wykorzystujemy wiersze macierzy zapisane na liście tymczasowej.
W efekcie otrzymujemy macierz posortowaną według kolumny z 2011 roku:
To jest najprostszy przykład jak sortować macierz poprzez tablicę jednowymiarową. Teraz wyobraźmy sobie, że mamy grupy dla pomiarów po lewej stronie i będziemy sortować w obrębie każdej grupy. Jak zauważono wcześniej, sortowanie wierszy nie jest opcją w tym przypadku. Przyjrzyjmy się jak posortować komórki.
Odwróćmy macierz z poprzedniego przykładu:
Tworzymy również obsługę zdarzenia ModifyResult dla macierzy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
public class ReportScript { public class DescendingComparer<T>: IComparer<T> where T : IComparable<T> { public int Compare(T x, T y) { return y.CompareTo(x); } } // the key of the pair is the value in the cell of the matrix, the value of the pair is the y-coordinate of the cell // the dictionary will be sorted by cell value in this way private SortedList<int, double> numbers = new SortedList<int, double>(); private void Matrix1_ModifyResult(object sender, EventArgs e) { int x = 3; int y = 2; Dictionary<int, double> cellsMonth = new Dictionary<int, double>(); Dictionary<int, double> cellsFirst = new Dictionary<int, double>(); Dictionary<int, double> cellsSecond = new Dictionary<int, double>(); Dictionary<int, double> cellsThird = new Dictionary<int, double>(); Dictionary<int, double> cellsFourth = new Dictionary<int, double>(); Dictionary<int, double> cellsTotal = new Dictionary<int, double>(); List<List<int>> allCells = new List<List<int>>(); bool other = false; int z = 2; double val2 = 0.0; var val3 = 0.0; string message = ""; List<string> years = new List<string>(); for (int j=0; j<Matrix1.ResultTable.RowCount; j++) { var column = Matrix1.ResultTable.Columns[0] as TableColumn; try { years.Add(Matrix1.ResultTable.GetCellData(0,j).Value.ToString()); } catch (Exception) {} } //We do the cycle for each year foreach (var year in years) { total = false; // We get the cell values for each year for a given column while (!total) { // We exclude Total being in the list of sorted values if (Matrix1.ResultTable.GetCellData(1,z).Text!="Total") { //Column of months var value = Matrix1.ResultTable.GetCellData(1,z).Value; if (value!=null) { Double.TryParse(value.ToString(),out val3); cellsMonth.Add(z,val3); } else cellsMonth.Add(z, 0.0); //Column for first employee value = Matrix1.ResultTable.GetCellData(2,z).Value; if (value!=null) { Double.TryParse(value.ToString(),out val3); cellsFirst.Add(z,val3); } else cellsFirst.Add(z, 0.0); //Column for the second employee value = Matrix1.ResultTable.GetCellData(3,z).Value; if (value!=null) { Double.TryParse(value.ToString(),out val3); cellsSecond.Add(z,val3); } else cellsSecond.Add(z, 0.0); //Column for the third employee value = Matrix1.ResultTable.GetCellData(5,z).Value; if (value!=null) { Double.TryParse(value.ToString(),out val3); cellsFourth.Add(z,val3); } else cellsFourth.Add(z, 0.0); //Sort column. It will serve as a sorting reference for other columns value = Matrix1.ResultTable.GetCellData(4,z).Value; if (value!=null) { Double.TryParse(value.ToString(),out val3); cellsThird.Add(z,val3); } else cellsThird.Add(z, 0.0); //Column for the fifth employee value = Matrix1.ResultTable.GetCellData(6,z).Value; if (value!=null) { Double.TryParse(value.ToString(),out val3); cellsTotal.Add(z,val3); } else cellsTotal.Add(z, 0.0); } else { total = true; } z++; } //Let’s sort the cellsThird by the list, which is the column for the third employee var keys = cellsThird.OrderByDescending(i=>i.Value).Select(key => key.Key).ToList(); //We set a new value for the cells in all strings in the required columns according to the order in the sorted dictionary for the third column int k = 0; foreach(var key in keys) { Matrix1.ResultTable.GetCellData(1, cellsThird.Keys.ElementAt(k)).Text = cellsMonth[key].ToString(); Matrix1.ResultTable.GetCellData(2, cellsThird.Keys.ElementAt(k)).Text = cellsFirst[key].ToString(); Matrix1.ResultTable.GetCellData(3, cellsThird.Keys.ElementAt(k)).Text = cellsSecond[key].ToString(); Matrix1.ResultTable.GetCellData(4, cellsThird.Keys.ElementAt(k)).Text = cellsThird[key].ToString(); Matrix1.ResultTable.GetCellData(5, cellsThird.Keys.ElementAt(k)).Text = cellsFourth[key].ToString(); Matrix1.ResultTable.GetCellData(6, cellsThird.Keys.ElementAt(k)).Text = cellsTotal[key].ToString(); k++; } cellsThird.Clear(); } } } |
Istnieją dwie zasadnicze różnice w stosunku do poprzedniej metody - sortujemy za pomocą zastępowania, a nie usuwania/insertowania, oraz sortujemy po każdej kolumnie osobno.
Z komentarzy w kodzie powinno być jasne, co i gdzie należy zrobić. Ale mimo to, zerknijmy szybko:
1) Po pierwsze, otrzymujemy wartości grup wymiarów, aby wiedzieć, ile zestawów sortujących potrzebujemy. Kolejność sortowania jest inna dla każdej grupy.
2) Następnie pobieramy dane dla wszystkich kolumn potrzebnych do sortowania. To znaczy dla wszystkich kolumn, z wyjątkiem pierwszej, która zawiera nazwy grup wymiarów.
3) Następnie wybieramy zestaw wartości wymaganych do sortowania i sortujemy go.
4) Można ułożyć komórki we wszystkich posortowanych kolumnach zgodnie z kolejnością tych indeksów, korzystając z wynikowego słownika, gdzie kluczem jest indeks komórki.
Wynikiem jest matryca posortowana dla Nancy Davolio:
W ten sposób można sortować macierz według dowolnej kolumny danych. Co więcej, można dokonać niestandardowego sortowania nie tylko w porządku malejącym lub rosnącym. Dodatkowo możesz wyłączyć z sortowania określone wiersze (sumaryczne lub obliczeniowe) ustawiając je w indywidualnej kolejności.