• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IgniteUI / igniteui-angular / 17299076362

28 Aug 2025 02:33PM UTC coverage: 91.611% (-0.03%) from 91.636%
17299076362

push

github

web-flow
Merge pull request #16049 from IgniteUI/rkaraivanov/performance-measure-service

wip: Grid(s) performance improvements

13669 of 16006 branches covered (85.4%)

103 of 103 new or added lines in 7 files covered. (100.0%)

8 existing lines in 2 files now uncovered.

27542 of 30064 relevant lines covered (91.61%)

33473.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

66.67
/projects/igniteui-angular/src/lib/data-operations/sorting-strategy.ts
1
import type { KeyOfOrString } from '../core/types';
2
import { IBaseEventArgs } from '../core/utils';
3
import { GridType } from '../grids/common/grid.interface';
4

5
/* mustCoerceToInt */
6
export enum SortingDirection {
3✔
7
    None = 0,
3✔
8
    Asc = 1,
3✔
9
    Desc = 2
3✔
10
}
11

12
/* marshalByValue */
13
/* tsPlainInterface */
14
export interface ISortingExpression<T = any> extends IBaseEventArgs {
15
    fieldName: KeyOfOrString<T> & string;
16
    /* mustCoerceToInt */
17
    dir: SortingDirection;
18
    ignoreCase?: boolean;
19
    strategy?: ISortingStrategy;
20
}
21

22
export interface ISortingStrategy {
23
    /* blazorSuppress */
24
    sort: (
25
        data: any[],
26
        fieldName: string,
27
        dir: SortingDirection,
28
        ignoreCase: boolean,
29
        valueResolver: (obj: any, key: string, isDate?: boolean) => any,
30
        isDate?: boolean,
31
        isTime?: boolean,
32
        grid?: GridType
33
    ) => any[];
34
}
35

36
export class DefaultSortingStrategy implements ISortingStrategy {
37
    protected static _instance: DefaultSortingStrategy = null;
3✔
38

39
    protected constructor() { }
40

41
    public static instance(): DefaultSortingStrategy {
42
        return this._instance || (this._instance = new this());
45,610✔
43
    }
44

45
    /* blazorSuppress */
46
    public sort(
47
        data: any[],
48
        fieldName: string,
49
        dir: SortingDirection,
50
        ignoreCase: boolean,
51
        valueResolver: (obj: any, key: string, isDate?: boolean) => any,
52
        isDate?: boolean,
53
        isTime?: boolean
54
    ) {
55
        const key = fieldName;
2,129✔
56
        const reverse = (dir === SortingDirection.Desc ? -1 : 1);
2,129✔
57

58
        /**
59
         * Use Schwartizian transform on the data before sorting it so that the sorting value
60
         * is not recomputed on every object compare which improves the number of comparisons from O(nlogn) to O(n)
61
         * where n is the length of the datasource.
62
         * This, on a very large dataset of 1 million records, gives a significant performance boost.
63
         */
64
        const resolver = valueResolver.bind(this);
2,129✔
65
        const preparedData = data.map(item => {
2,129✔
66
            return {
1,024,684✔
67
                original: item,
68
                sortValue: this.prepareSortValue(resolver(item, key, isDate, isTime), ignoreCase)
69
            }
70
        });
71
        const compareFn = (a, b) => reverse * this.compareValues(a.sortValue, b.sortValue);
1,035,328✔
72
        preparedData.sort(compareFn);
2,129✔
73

74
        return preparedData.map(item => item.original);
1,024,684✔
75
    }
76

77
    public compareValues(a: any, b: any): number {
78
        const aIsNullish = a == null;
2,051,518✔
79
        const bIsNullish = b == null;
2,051,518✔
80

81
        if (aIsNullish && bIsNullish) return 0;
2,051,518✔
82
        if (aIsNullish) return -1
2,050,699✔
83
        if (bIsNullish) return 1;
2,049,189✔
84

85
        return a > b ? 1 : a < b ? -1 : 0;
2,048,659✔
86
    }
87

88
    protected compareObjects(
89
        obj1: any,
90
        obj2: any,
91
        key: string,
92
        reverse: number,
93
        ignoreCase: boolean,
94
        valueResolver: (obj: any, key: string, isDate?: boolean, isTime?: boolean) => any,
95
        isDate: boolean,
96
        isTime: boolean
97
    ) {
UNCOV
98
        let a = valueResolver.call(this, obj1, key, isDate, isTime);
×
UNCOV
99
        let b = valueResolver.call(this, obj2, key, isDate, isTime);
×
UNCOV
100
        if (ignoreCase) {
×
UNCOV
101
            a = a && a.toLowerCase ? a.toLowerCase() : a;
×
UNCOV
102
            b = b && b.toLowerCase ? b.toLowerCase() : b;
×
103
        }
UNCOV
104
        return reverse * this.compareValues(a, b);
×
105
    }
106

107
    protected arraySort(data: any[], compareFn?: (arg0: any, arg1: any) => number): any[] {
108
        return data.sort(compareFn);
4✔
109
    }
110

111
    protected prepareSortValue(value: any, ignoreCase: boolean) {
112
        return ignoreCase && typeof value === 'string' ? value.toLocaleLowerCase() : value;
1,024,684✔
113
    }
114
}
115

116
export class GroupMemberCountSortingStrategy implements ISortingStrategy {
117
    protected static _instance: GroupMemberCountSortingStrategy = null;
3✔
118

119
    protected constructor() { }
120

121
    public static instance(): GroupMemberCountSortingStrategy {
122
        return this._instance || (this._instance = new this());
×
123
    }
124

125
    public sort(data: any[], fieldName: string, dir: SortingDirection) {
126
        const groupedArray = this.groupBy(data, fieldName);
×
127
        const reverse = (dir === SortingDirection.Desc ? -1 : 1);
×
128

129
        const cmpFunc = (a, b) => {
×
130
            return this.compareObjects(a, b, groupedArray, fieldName, reverse);
×
131
        };
132

133
        return data
×
134
            .sort((a, b) => a[fieldName].localeCompare(b[fieldName]))
×
135
            .sort(cmpFunc);
136
    }
137

138
    public groupBy(data, key) {
139
        return data.reduce((acc, curr) => {
×
140
            (acc[curr[key]] = acc[curr[key]] || []).push(curr);
×
141
            return acc;
×
142
        }, {})
143
    }
144

145
    protected compareObjects(obj1: any, obj2: any, data: any[], fieldName: string, reverse: number) {
146
        const firstItemValuesLength = data[obj1[fieldName]].length;
×
147
        const secondItemValuesLength = data[obj2[fieldName]].length;
×
148

149
        return reverse * (firstItemValuesLength - secondItemValuesLength);
×
150
    }
151
}
152

153
export class FormattedValuesSortingStrategy extends DefaultSortingStrategy {
154
    protected static override _instance: FormattedValuesSortingStrategy = null;
3✔
155

156
    constructor() {
157
        super();
1✔
158
    }
159

160
    public static override instance(): FormattedValuesSortingStrategy {
161
        return this._instance || (this._instance = new this());
×
162
    }
163

164
    public override sort(
165
        data: any[],
166
        fieldName: string,
167
        dir: SortingDirection,
168
        ignoreCase: boolean,
169
        valueResolver: (obj: any, key: string, isDate?: boolean) => any,
170
        isDate?: boolean,
171
        isTime?: boolean,
172
        grid?: GridType
173
    ) {
174
        const key = fieldName;
4✔
175
        const reverse = (dir === SortingDirection.Desc ? -1 : 1);
4✔
176
        const cmpFunc = (obj1: any, obj2: any) => this.compareObjects(obj1, obj2, key, reverse, ignoreCase, valueResolver, isDate, isTime, grid);
253✔
177
        return this.arraySort(data, cmpFunc);
4✔
178
    }
179

180
    protected override compareObjects(
181
        obj1: any,
182
        obj2: any,
183
        key: string,
184
        reverse: number,
185
        ignoreCase: boolean,
186
        valueResolver: (obj: any, key: string, isDate?: boolean, isTime?: boolean) => any,
187
        isDate: boolean,
188
        isTime: boolean,
189
        grid?: GridType
190
    ) {
191
        let a = valueResolver.call(this, obj1, key, isDate, isTime);
253✔
192
        let b = valueResolver.call(this, obj2, key, isDate, isTime);
253✔
193

194
        if (grid) {
253✔
195
            const col = grid.getColumnByName(key);
253✔
196
            if (col && col.formatter) {
253✔
197
                a = col.formatter(a);
253✔
198
                b = col.formatter(b);
253✔
199
            }
200
        }
201

202
        if (ignoreCase) {
253✔
203
            a = a && a.toLowerCase ? a.toLowerCase() : a;
253!
204
            b = b && b.toLowerCase ? b.toLowerCase() : b;
253!
205
        }
206
        return reverse * this.compareValues(a, b);
253✔
207
    }
208
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc