Splitting rows in a 2d array so there is only 1 non-zero value

OBram

New Member
Joined
Mar 24, 2025
Messages
3
Office Version
  1. 365
Platform
  1. Windows
Hello from NZ!

I have been struggling away trying to write a user defined function and thought I'd see if anyone here could help.

My goal is to take a 2d array and split out any rows with more than 1 non-zero value into two rows. See the images below for an example of what I'm looking to accomplish.


Input Array:
1742873150169.png


Desired Output Array:
1742873234156.png



I'm open to any solutions for this, but it needs to be dynamic as the workbook updates so either built in functions or custom functions.

Here is what I have so far in my custom function (helped by chatGPT). It keeps giving VALUE errors when I try to use it and when I added a driver function to step through it I was getting Subscript out of range errors. Any help would be awesome!

Function SplitRows(arr As Variant) As Variant
Dim i As Long, j As Long, k As Long
Dim nonZeroCount As Long
Dim result() As Variant
Dim resultRow As Long
Dim numRows As Long
Dim numCols As Long


' Determine the number of rows and columns in the input array
numRows = UBound(arr, 1)
numCols = UBound(arr, 2)

' Initialize the result array with enough rows
ReDim result(1 To 1, 1 To numCols)
resultRow = 1

For i = LBound(arr, 1) To numRows
' Count non-zero values in the current row
nonZeroCount = 0
For j = LBound(arr, 2) To numCols
If arr(i, j) <> 0 Then
nonZeroCount = nonZeroCount + 1
End If
Next j

' If there are no non-zero values, copy the row as is
If nonZeroCount = 0 Then
If resultRow > UBound(result, 1) Then
ReDim Preserve result(1 To resultRow, 1 To numCols)
End If
For j = LBound(arr, 2) To numCols
result(resultRow, j) = arr(i, j)
Next j
resultRow = resultRow + 1
Else
' Create rows for each non-zero value
For j = LBound(arr, 2) To numCols
If arr(i, j) <> 0 Then
If resultRow > UBound(result, 1) Then
ReDim Preserve result(1 To resultRow, 1 To numCols)
End If
For k = LBound(arr, 2) To numCols
If k = j Then
result(resultRow, k) = arr(i, k)
Else
result(resultRow, k) = 0
End If
Next k
resultRow = resultRow + 1
End If
Next j
End If
Next i

SplitRows = result
End Function
 
The example above needs to split a row with two non-zero values into two unique rows, but in would ideally work if a row had three non-zero values by splitting it into three unique rows, etc. Also apologies for the poor formatting above, hopefully the below is more readable

VBA Code:
Function SplitRows(arr As Variant) As Variant
    Dim i As Long, j As Long, k As Long
    Dim nonZeroCount As Long
    Dim result() As Variant
    Dim resultRow As Long
    Dim numRows As Long
    Dim numCols As Long
    
    
    ' Determine the number of rows and columns in the input array
    numRows = UBound(arr, 1)
    numCols = UBound(arr, 2)
    
    ' Initialize the result array with enough rows
    ReDim result(1 To 1, 1 To numCols)
    resultRow = 1
    
    For i = LBound(arr, 1) To numRows
        ' Count non-zero values in the current row
        nonZeroCount = 0
        For j = LBound(arr, 2) To numCols
            If arr(i, j) <> 0 Then
                nonZeroCount = nonZeroCount + 1
            End If
        Next j
        
        ' If there are no non-zero values, copy the row as is
        If nonZeroCount = 0 Then
            If resultRow > UBound(result, 1) Then
                ReDim Preserve result(1 To resultRow, 1 To numCols)
            End If
            For j = LBound(arr, 2) To numCols
                result(resultRow, j) = arr(i, j)
            Next j
            resultRow = resultRow + 1
        Else
            ' Create rows for each non-zero value
            For j = LBound(arr, 2) To numCols
                If arr(i, j) <> 0 Then
                    If resultRow > UBound(result, 1) Then
                        ReDim Preserve result(1 To resultRow, 1 To numCols)
                    End If
                    For k = LBound(arr, 2) To numCols
                        If k = j Then
                            result(resultRow, k) = arr(i, k)
                        Else
                            result(resultRow, k) = 0
                        End If
                    Next k
                    resultRow = resultRow + 1
                End If
            Next j
        End If
    Next i
    
    SplitRows = result
End Function
 
Upvote 0
Hello from NZ!
Welcome to the Forum, and hello from OZ!

How about:

VBA Code:
Function SplitRows(rng As Range) As Double()

    Dim vIn As Variant
    Dim dOut() As Double
    Dim N As Long, i As Long, j As Long, count As Long
   
    vIn = rng.Value2
    N = Application.CountIf(rng, "<>0")
    ReDim dOut(1 To N, 1 To UBound(vIn, 2))
   
    For i = 1 To UBound(vIn)
        For j = 1 To UBound(vIn, 2)
            If vIn(i, j) <> 0 Then
                count = count + 1
                dOut(count, j) = vIn(i, j)
            End If
        Next j
    Next i
   
    SplitRows = dOut

End Function
ABCDE
1In
210000
300230
404000
500005
600600
700000
870089
9
10Out
1110000
1200200
1300030
1404000
1500005
1600600
1770000
1800080
1900009
Sheet1
Cell Formulas
RangeFormula
A11:E19A11=SplitRows(A2:E8)
Dynamic array formulas.
 
Last edited:
Upvote 0
Solution
If you'd like a nice, long, complicated formula, you can try this LAMBDA function. Put this formula in the Name Manager with a name of ExpArray:

Excel Formula:
=LAMBDA(arr,[currow],[nza],[resarr],
     LET(rws,ROWS(arr),
             IF(rws=0,"Empty array",
                             LET(rs,IF(ISOMITTED(resarr),0,resarr),
                                     cr,IF(ISOMITTED(nza),currow+1,
                                                                          currow),
                                         IF(cr>rws,DROP(rs,1),
                                                         LET(ix,INDEX(arr,cr,0),
                                                                 s,SEQUENCE(,COLUMNS(ix)),
                                                              nzx,IF(cr>currow,LET(nzy,IF(ix<>0,s,0),FILTER(nzy,nzy<>0)),
                                                                                          nza),
                                                                 f,FILTER(ix,ix<>0),
                                                                 IF(COUNT(f)<2,ExpArray(arr,cr,,VSTACK(rs,ix)),
                                                                                         LET(v,IF(s=INDEX(nzx,1),ix,
                                                                                                                                   0),
                                                                                               IF(COLUMNS(nzx)=1,ExpArray(arr,cr,,VSTACK(rs,v)),
                                                                                                                                   ExpArray(arr,cr,DROP(nzx,,1),VSTACK(rs,v)))))))))))

It works like this:

Book1
ABCDEFGHIJ
1
2
303300000000
40000000000
50001.102.20000
60000000050
7101002203300000
800000001800
9
10
1103300000000
120000000000
130001.1000000
14000002.20000
150000000050
161010000000000
1700220000000
1800003300000
1900000001800
20
Sheet19
Cell Formulas
RangeFormula
A11:J19A11=ExpArray(A3:J8)
Dynamic array formulas.
Lambda Functions
NameFormula
ExpArray=LAMBDA(arr,[currow],[nza],[resarr],LET(rws,ROWS(arr),IF(rws=0,"Empty array",LET(rs,IF(ISOMITTED(resarr),0,resarr),cr,IF(ISOMITTED(nza),currow+1,currow),IF(cr>rws,DROP(rs,1),LET(ix,INDEX(arr,cr,0),s,SEQUENCE(,COLUMNS(ix)),nzx,IF(cr>currow,LET(nzy,IF(ix<>0,s,0),FILTER(nzy,nzy<>0)),nza),f,FILTER(ix,ix<>0),IF(COUNT(f)<2,ExpArray(arr,cr,,VSTACK(rs,ix)),LET(v,IF(s=INDEX(nzx,1),ix,0),IF(COLUMNS(nzx)=1,ExpArray(arr,cr,,VSTACK(rs,v)),ExpArray(arr,cr,DROP(nzx,,1),VSTACK(rs,v)))))))))))


Depending on how your table is updated, this can be made dynamic to read whatever size of a table you have.
 
Upvote 0
Welcome to the MrExcel board!

I have tried a formula approach too. My sample data is a slight modification of Eric's since, as far as I can see, the OP does not have any rows that do not have at least 1 non-zero value.

25 03 25.xlsm
ABCDEFGHIJ
1
2
303300000000
40001.102.20000
50000000050
6101002203300000
700000001800
8
9
10
1103300000000
120001.1000000
13000002.20000
140000000050
151010000000000
1600220000000
1700003300000
1800000001800
OBram (2)
Cell Formulas
RangeFormula
A11:J18A11=LET(d,A3:J7,t,INDEX(d,1,1),k,10^6,tc,TOCOL((ROW(d)-ROW(t)+1)*k+COLUMN(d)-COLUMN(t)+1/(d<>0),2), MAKEARRAY(ROWS(tc),COLUMNS(d),LAMBDA(r,c,LET(j,INDEX(tc,r),IFERROR(INDEX(d,INT(j/k),IF(MOD(j,k)=c,c,x)),0)))))
Dynamic array formulas.
 
Upvote 0
It always fascinates me when so many different algorithms come from the same problem. Peter's formula is far different from my original LAMBDA. But Peter's formula inspired me to try a different approach. Consider this:

Book1
ABCDEFGHIJ
1
2
303300000000
40001.102.20000
50000000050
6101002203300000
700000001800
8
9
1003300000000
110001.1000000
12000002.20000
130000000050
141010000000000
1500220000000
1600003300000
1700000001800
18
Sheet20
Cell Formulas
RangeFormula
A10:J17A10=LET(d,A3:J7,tc,TOCOL(d),s,SEQUENCE(ROWS(tc)),w,COLUMNS(d),co,IF(tc,MOD(s-1,w)+1,0),v,FILTER(tc,tc>0),cols,FILTER(co,co>0),c,SEQUENCE(,w),v*(c=cols))
Dynamic array formulas.


Like Peter's, any row consisting entirely of zeros will be removed.
 
Upvote 0
Welcome to the Forum, and hello from OZ!

How about:

VBA Code:
Function SplitRows(rng As Range) As Double()

    Dim vIn As Variant
    Dim dOut() As Double
    Dim N As Long, i As Long, j As Long, count As Long
   
    vIn = rng.Value2
    N = Application.CountIf(rng, "<>0")
    ReDim dOut(1 To N, 1 To UBound(vIn, 2))
   
    For i = 1 To UBound(vIn)
        For j = 1 To UBound(vIn, 2)
            If vIn(i, j) <> 0 Then
                count = count + 1
                dOut(count, j) = vIn(i, j)
            End If
        Next j
    Next i
   
    SplitRows = dOut

End Function
ABCDE
1In
210000
300230
404000
500005
600600
700000
870089
9
10Out
1110000
1200200
1300030
1404000
1500005
1600600
1770000
1800080
1900009
20
Sheet1
Cell Formulas
RangeFormula
A11:E19A11=SplitRows(A2:E8)
Dynamic array formulas.
Worked a treat thanks, legend.
 
Upvote 0

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top