Horizontally merge adjacent cells in one or more rows.
Compatibility
The macro can be used in the highlighted input document types only. A greyed-out icon indicates not supported.
Usage
Merges adjacent cells within a row that either have identical text, contain specified text, or do not contain specified text. Use 'matchType' to control how cells are matched. Cells already part of a merge are skipped by default (controlled by 'skipMergedCells'). The leftmost cell's style is always retained. Row targeting: use 'rowIndices' for specific rows, or 'rowIndexFrom'/'rowIndexTo' for a range. These are mutually exclusive. Column scope: use 'startColumnIndex'/'endColumnIndex' to restrict which columns participate.
Updates
The help examples now build their tables with [List.Table:], so each example can be copied into a report and run as it is.
History (3)
In Report Studio and HTML, 'mergeValue=Empty' now works on cells that contain formatting, such as bold text or a link. Previously those cells failed with an error and were left unmerged, while plain text cells merged correctly.
In Report Studio and HTML, a table that contains a nested table is now handled correctly: the nested table's rows and cells are no longer counted as belonging to the outer table, so row and column numbers match the table the author can see and a nested table is never changed by a macro aimed at the table around it.
In Report Studio and HTML, 'joinDelimiter' and 'matchText' are now treated as literal text, so markup characters in them are shown as characters. Previously a delimiter containing markup, such as a line break, silently discarded every value it was meant to join.
Behaviour (10)
| Parameter | Type | Presence | Purpose | Options | Default |
| List<ExecutionResult> | Optional | If specified, asserts the expected execution result of the macro. Accepts a single value or a semicolon-separated list of acceptable values (e.g. 'Success;Warning'). The macro executes normally; if the actual result matches any of the desired values, the result is converted to Success. If the actual result does not match, the result is converted to MacroError with a descriptive message. This is primarily used for testing and diagnostic purposes. Valid values are: 'Unknown', 'Success', 'MacroError', 'WorkerStopped', 'Running', 'Warning', 'NeverRun', 'Cancelled', 'Pending', 'Paused', 'SystemError', 'Deferred' and 'Stopped'. |
|
N/A | |
| Boolean | Optional | Should NCalc expression evaluation throw error on Overflow |
|
true | |
| String | Optional | If specified, asserts the expected output type of the macro result. The macro executes normally; if the actual type does not match, a macro error is generated. Requires 'storeAs', 'storeAsHidden', or 'storeFormattedValueAs' to be set for typed validation. Valid types include CLR names (e.g. Int32, Int64, Single, Double, Boolean, String, JArray, JObject) and C# keyword aliases (e.g. int, long, float, double, bool, string, uint, ulong, short, ushort, byte, sbyte, decimal, char, object). The special value 'Number' matches any numeric type. | N/A | N/A | |
| String | Optional | If specified, asserts the expected output value of the macro result. The macro executes normally; if the actual value does not match, a macro error is generated. When 'storeAs' or 'storeAsHidden' is set, the stored variable value is compared. Otherwise, the document output text is compared. | N/A | N/A | |
| String | Optional | The condition that must be true in order for the macro to be executed/evaluated. Must either evaluate to true or false, for example: "3+5=8" or "contains('abcd', 'z')". | N/A | true | |
| Boolean | Optional | Whether to skip the header (first) row when processing all rows. Has no effect when 'rowIndices' or 'rowIndexFrom' is specified. |
|
true | |
| MacroMode | Optional | The mode in which variables are stored. In legacy mode (default for Schedules), the variable created is a string and formatted. In normal mode (default for Report Studio), the output variable is stored as a strongly-typed object, e.g. an Int32 or a JArray etc., rather than a formatted string. |
|
Legacy | |
| ObfuscationType | Optional | Obfuscation type. Use obfuscation to write reports where sensitive data is hidden. When used, ReportMagic guarantees that the same input string will map to the same output string for the whole of the report (but the next time the report runs, it will most likely map to a different value). If you use obfuscation, the property in your macro will not show up and instead, you will see a fake item of the obfuscation type chosen. |
|
None | |
| Boolean | Optional | Whether to skip cells that are already part of a merge (acting as barriers). If false, encountering a pre-merged cell will throw an error. |
|
true | |
| String | Optional | If specified, adds a warning message for this macro. This is processed as an NCalc, and the warning message will ALWAYS be present and will be the value of the evaluated NCalc expression. | N/A | N/A |
Filtering & Sorting (2)
| Parameter | Type | Presence | Purpose | Options | Default |
| String | Optional | The text to match against (or a regular expression when 'matchType' is 'Regex'). Required when 'matchType' is 'Contains', 'NotContains', or 'Regex'. | N/A | N/A | |
| MergeRowMatchType | Optional | How cells are matched for merging. 'Identical' merges adjacent cells with the same text. 'Contains' merges adjacent cells whose text contains the 'matchText' value. 'NotContains' merges adjacent cells whose text does not contain the 'matchText' value. 'Regex' merges adjacent cells whose text matches the 'matchText' regular expression. |
|
Identical |
Output (3)
| Parameter | Type | Presence | Purpose | Options | Default |
| String | Optional | The text to display should the macro fail to execute. Note that a poorly-specified macro (e.g. omitting mandatory parameters) will still result in an error message. | N/A | N/A | |
| String | Optional | The name of a variable to create should the macro fail to execute. The variable will be a text variable, and will contain either the failure text (only if the failureText parameter is set), otherwise it will contain the exception / failure message. | N/A | N/A | |
| String | Optional | The delimiter to use when 'mergeValue' is 'Joined'. This is literal text: markup characters in it are shown as characters, not treated as HTML. | N/A | , |
General (7)
| Parameter | Type | Presence | Purpose | Options | Default |
| String | Optional | Add a comment to make your document template more readable. The comment is discarded in the output document. | N/A | N/A | |
| Int32 | Optional | The 1-based rightmost column to consider for merging. If omitted, uses the last column. | N/A | N/A | |
| MergeRowValueBehavior | Optional | What content the merged cell should contain. 'First' keeps the leftmost cell's text. 'Last' keeps the rightmost cell's text. 'Empty' clears the content. 'Joined' joins all cell texts with the 'joinDelimiter' delimiter. 'MatchText' uses the 'matchText' value as the content (only valid with 'Contains' or 'Regex' match type). |
|
First | |
| Int32 | Optional | The starting row index (1-based) for a range of rows to process. Mutually exclusive with 'rowIndices'. If specified without 'rowIndexTo', processes from this row to the last row. | N/A | N/A | |
| Int32 | Optional | The ending row index (1-based, inclusive) for a range of rows to process. Only valid with 'rowIndexFrom'. | N/A | N/A | |
| List<Int32> | Optional | The row indices (1-based, separated by a semi-colon) to process. Mutually exclusive with 'rowIndexFrom'/'rowIndexTo'. If omitted (and no range specified), all rows are processed. | N/A | N/A | |
| Int32 | Optional | The 1-based leftmost column to consider for merging. |
|
1 |
Examples (8)
Example 1
This example merges adjacent cells with identical text in all rows (header row is skipped by default):
[List.Table: values=H1^H2^H3^H4;a^a^a^b;c^c^d^d]
[Table.MergeRows:]Example 2
This example merges cells containing '2025' in the header row only, replacing content with the match text:
[List.Table: values=Region^Q1 2025^Q2 2025^Q3 2025^Q1 2026^Q2 2026;North^10^20^30^40^50]
[Table.MergeRows: rowIndices=1, matchType=Contains, matchText=2025, mergeValue=MatchText]Example 3
This example merges identical cells in columns 2 to 6 for rows 2 through 4:
[List.Table: values=Name^Mon^Tue^Wed^Thu^Fri;Alice^x^x^x^y^y;Bob^a^a^b^b^b;Carol^z^z^z^z^z]
[Table.MergeRows: rowIndexFrom=2, rowIndexTo=4, startColumnIndex=2, endColumnIndex=6]Example 4
This example merges non-'Total' cells and joins their values with ' + ':
[List.Table: values=C1^C2^C3^C4^C5;Total^100^200^Total^300]
[Table.MergeRows: rowIndices=2, matchType=NotContains, matchText=Total, mergeValue=Joined, joinDelimiter=" + "]Example 5
This example clears the content of merged cells using mergeValue=Empty:
[List.Table: values=H1^H2^H3;a^a^b;c^c^c]
[Table.MergeRows: mergeValue=Empty]Example 6
This example merges cells matching a regex pattern (starting with 'Q' followed by a digit) in the header row:
[List.Table: values=Q1 2025^Q2 2025^H1 2025^Q1 2026;10^20^30^40]
[Table.MergeRows: rowIndices=1, matchType=Regex, matchText=^Q\d, mergeValue=First]Example 7
This example merges the identical 'Green' cells in a data row, leaving 'Red' on its own:
[List.Table: values=Status^Q1^Q2^Q3;Green^Green^Green^Red]
[Table.MergeRows:]Example 8
This example merges the '2025' header cells from column 3 onwards, using startColumnIndex:
[List.Table: values=Info^Info^Jan 2025^Feb 2025^Mar 2025^Jan 2026;Region^Region^100^150^200^300]
[Table.MergeRows: rowIndices=1, matchType=Contains, matchText=2025, mergeValue=MatchText, startColumnIndex=3]