As you can see in the image, there are some 1 and 0s rearranged in 3 rows and one English Alphabet for each column. What I need to do is concatenate the English Alphabets for each row when the respective column value is 0. How can I do it?
Here is a VBA solution that can handle any number of columns (assuming that the letter associated with each column is the standard column label):
Function ZeroColumns(R As Range) As String
Dim n As Long
Dim count As Long
Dim cols As Variant
Dim cell As Range
n = R.Cells.count
ReDim cols(1 To n)
For Each cell In R.Cells
If cell.Value = 0 Then
count = count + 1
cols(count) = Split(cell.Address, "$")(1)
End If
Next cell
ReDim Preserve cols(1 To count)
ZeroColumns = Join(cols, "")
End Function
The code shouldn't be too hard to tweak if the stated assumption doesn't hold.
Conditionally Concatenate Row (UDF)
Arguments
SourceRowRange: The range containing the values that will be
written toCCROW e.g. A, B, C ... Required.
CriteriaRowRange: The range that will be checked for
CriteriaValue. Required.
CriteriaValue: The value that the cells in CriteriaRowRange will
be checked against. Default is 0. Optional.
JoinString: The value that will be put between the values that will
be written to CCROW. Default is "". Optional.
' Copy the following code to a standard module i.e. in VBE go to Insert>Module.
The Code
Function CCROW(ByVal SourceRowRange As Range, ByVal CriteriaRowRange As Range, _
Optional ByVal CriteriaValue As Variant = 0, _
Optional ByVal JoinString As String) As String
Dim vntS As Variant ' Source Array
Dim vntC As Variant ' Criteria Array
Dim NoC As Long ' Number of Columns
Dim j As Long ' Arrays Column Counter
Dim strB As String ' String Builder
Dim strC As String ' Criteria String
' Calculate number of columns of the narrower Range.
NoC = WorksheetFunction.Min(SourceRowRange.Columns.count, _
CriteriaRowRange.Columns.count)
' Copy resized (adjust them to same size) Ranges to Arrays.
vntS = SourceRowRange.Resize(1, NoC)
vntC = CriteriaRowRange.Resize(1, NoC)
' Loop through columns of either Array.
For j = 1 To NoC
' Write current value of Criteria Array to Criteria String.
strC = vntC(1, j)
' Check if Criteria String is NOT empty.
If strC <> "" Then
' Check if Criteria String is equal to Criteria Value.
If strC = CriteriaValue Then
' Check if String Builder is NOT empty.
If strB <> "" Then ' NOT empty.
strB = strB & JoinString & vntS(1, j)
Else ' IS empty (only once).
strB = vntS(1, j)
End If
End If
End If
Next
' Write String Builder to Conditionally Concatenate Row.
CCROW = strB
End Function
Usage in Excel
=CCROW(A$1:I$1,A3:I3) ' Result: ADG
=CCROW(A$1:I$1,A4:I4) ' Result: CFI
=CCROW(A$1:I$1,A5:I5) ' Result: DG
If you add JoinString:
=CCROW(A$1:I$1,A3:I3,,",") ' Result: A,D,G
=CCROW(A$1:I$1,A3:I3,0,",") ' Result: A,D,G
=CCROW(A$1:I$1,A3:I3,0,", ") ' Result: A, D, G
IF you change CriteriaValue:
=CCROW(A$1:I$1,A3:I3,1) ' Result: BCEFHI
=CCROW(A$1:I$1,A4:I4,1) ' Result: ABDEGH
=CCROW(A$1:I$1,A5:I5,1) ' Result: ABCEFHI
Remarks
Lock ($) the row of SourceRowRange to keep it the same when the formula is copied down.
You can do it all in one formula if you like:
=CONCATENATE(IF($A1=0,'A',''),IF($B1=0,'B',''), ...)
Or put the intermediate strings in a separate row and then concatenate them (to save wear and tear on your fingers).
Are you going to this to many more columns, or just the ones you've mentioned? As long as the number of columns is relatively small, as in your picture, you can concatenate IF functions to achieve your result.
Here's what I did:
Using that formula will get you a result like the one you have:
Assuming also that you have the values in a worksheet like mine, just paste the formula =IF(B3=1,"",B$1)&IF(C3=1,"",C$1)&IF(D3=1,"",D$1)&IF(E3=1,"",E$1)&IF(F3=1,"",F$1)&IF(G3=1,"",G$1)&IF(H3=1,"",H$1)&IF(I3=1,"",I$1)&IF(J3=1,"",J$1)
in B7 and then drag to B8 and B9 to get the rest of the results.
Of course, if you are going to do this for many more columns, it's maybe best to use VBA.
Here, add this function to a module.
You can then call it directly via excel. Nice one.
Function conc(ref As Range, Optional Separator As String) As String
Dim Cell As Range
Dim Result As String
For Each Cell In ref
If Cell.Value = 0 Then
Result = Result & chr(64 + Cell.Column) & Separator
End If
Next Cell
If Separator <> "" Then conc = Left(Result, Len(Result) - 1) Else: conc = Result
End Function
The following array formula will do the job (enter it with Ctrl+Shift+Enter):
=CONCAT(IF($A1:$I1=0,UNICHAR(64+COLUMN($A1:$I1)),""))
For older Excel versions, use the legacy functions CONCATENATE() and CHAR() in place of these functions.
Related
I want to use a VBA function ScopeSum() in an Excel table, such function is checking the "1" values on the same row & then sum relevant header's values.
"SopeH" is named header range.
I've to use this function on the same column (column "P" for the below example) for 100's of rows.
If I copy the function cell & fill all rows the result is as the first cell, but if I edit it, it works fine.
Function ScopeSum() As String
Dim i As Integer
Dim j As Long
Dim rng As Range
Dim cur_rng As Range
Dim ScopeText As String
Dim cell As Variant
Set rng = Range("ScopeH")
j = Range(ActiveCell.Address).Row
Set cur_rng = Range("ScopeH").Offset(j - 2, 0)
i = 0
ScopeText = ""
For Each cell In cur_rng.Cells
i = i + 1
If UCase(cell.Value) = 1 Then ScopeText = ScopeText & ", " & Application.WorksheetFunction.Index(rng, 1, i)
Next
ScopeSum = ScopeText
End Function
Excel Table
After refreshing the page
Make sure you submit the data and header ranges as parameters, so the UDF (user defined function) works for any data range and depends on the data range. Otherwise your formula would not update automatically if the data changes.
Option Explicit
Public Function ScopeSum(ByVal DataRange As Range, ByVal HeaderRange As Range) As String
Dim Data() As Variant ' read data into array
Data = DataRange.Value
Dim Header() As Variant ' read header into array
Header = HeaderRange.Value
Dim Result As String ' collect results for output here
Dim iCol As Long
For iCol = 1 To UBound(Data, 2) ' loop through data and concatenate headers
If Data(1, iCol) = 1 Then
Result = Result & IIf(Result <> vbNullString, ", ", vbNullString) & Header(1, iCol)
End If
Next iCol
ScopeSum = Result ' output results
End Function
Then use the following formula in cell P3:
=ScopeSum(Q3:Z3,$Q$2:$Z$2)
Make sure the header is fixed with the $ signs in the formula. And copy it down:
This has the advantage that you never need to change the code, even if the ranges change. Also you could easily add an Item 11 without changing the code by just adjusting the ranges in the formula.
I have a spreadsheet with two columns, key and name. Right now, the name gets repeated multiple times per value. I set up a formula to get unique values in name, but now I need to get a list of all the values of the cells that match that column.
So if I have the name 'Brian', and there are 4 of them with index values of 5, 6, 7, and 8, I need a formula to take/use the value of C1 to look at B:B and give me all values in A:A.
Is this possible?
In your google-sheets's D1 put this formula in and drag down.
=arrayformula(textjoin(" ", true, if(B:B=C1, A:A, "")))
CONCIF (UDF)
In Excel
=CONCIF($B$1:$B$20,$C1,$A$1:$A$20)
In VBA
Option Explicit
Function CONCIF(MatchRange As Range, ByVal MatchValue As Variant, _
ConcatRange As Range, _
Optional ByVal Delimiter As String = " ") As String
Dim vntM As Variant ' Match Array
Dim vntC As Variant ' Concat Array
Dim Nor As Long ' Number of Rows
Dim i As Long ' Row Counter
Dim strC As String ' Concat String
Dim strR As String ' Result String
' Check number of rows in MatchRange is less than or equal to number
' of rows in Concat Range.
If MatchRange.Rows.Count <= ConcatRange.Rows.Count Then
' Write number of rows in MatchRange to Number of Rows.
Nor = MatchRange.Rows.Count
Else
' Write number of rows in ConcatRange to Number of Rows.
Nor = ConcatRange.Rows.Count
End If
' Check if Number of Rows is equal to 1, which would mean there
' can only be one match.
If Nor = 1 Then
' Check if the value in 1-cell MatchRange is equal to MatchValue.
If MatchRange.Cells(1, 1) = MatchValue Then
' Write value of 1-cell ConcatRange, converted to string, to CONCIF.
CONCIF = CStr(ConcatRange.Cells(1, 1))
End If
Exit Function
End If
' Copy the range defined by 1st Nor number of cells in 1st column of
' MatchRange to 2D 1-based 1-column Match Array.
vntM = MatchRange.Cells(1, 1).Resize(Nor)
' Copy the range defined by 1st Nor number of cells in 1st column of
' ConcatRange to 2D 1-based 1-column Concat Array.
vntC = ConcatRange.Cells(1, 1).Resize(Nor)
' Loop through elements (rows) of Match/Concat Arrays.
For i = 1 To Nor
' Check if current value in MatchArray is equal to MatchValue.
If vntM(i, 1) = MatchValue Then
' Write current value in ConcatArray to Concat String.
strC = CStr(vntC(i, 1))
' Check if Concat String is NOT "".
If strC <> "" Then
' Check if Result String is NOT "".
If strR <> "" Then
' Concatenate current value of Result String, Delimiter
' and Concat String, to Result String.
strR = strR & Delimiter & strC
Else
' Write Concat String to Result String (only once).
strR = strC
End If
End If
End If
Next
' Write Result String to CONCIF.
CONCIF = strR
End Function
I have a special case where I need to count a specific number from a range of cells or a column, which will look like this
1 A
2 1,2,3
3 1,4,5
4 1,3,5,6
I need to count the "1" alone from this column A. Same way for every other numbers e.g., '2', '3' etc..
I have tried the following code, however it gives me the unique numbers count from a single cell
Public Function Count(r As Range) As Long
Dim c As Collection
Set c = New Collection
ary = Split(r.Text, ",")
On Error Resume Next
For Each a In ary
c.Add a, CStr(a)
If Err.Number = 0 Then
Count = Count + 1
Else
Err.Number = 0
End If
Next a
On Error GoTo 0
End Function`
How do I change this to a range as well as only specific to counting one number from that range?
You can do something like this:
Public Function CountNum(rng As Range, num) As Long
Dim rv As Long, c As Range, arr, a
num = CStr(num)
For Each c In rng.Cells
If Len(c.Value) > 0 Then
arr = Split(c.Value, ",")
For Each a In arr
If a = num Then rv = rv + 1
Next a
End If
Next c
CountNum = rv
End Function
To call (for example):
=countnum(A2:A4,1)
Count Delimited String Occurrences (UDF)
The Code
'***********************************************************************
' Title: Count Delimited String Occurrences
' Purpose: Counts the number of occurrences of a value in delimited parts
' of cells of a range containing not numeric values.
' Inputs:
' CountRange: Required. The range which cells to search.
' CountValue: Required. The value to search for. Variant.
' CountDelimiter: Optional. The delimiter by which each part of each
' cell will be checked against CountValue. Default is ",".
' CompareBinary0Text1: Optional. The method how the check will be
' performed. (Binary) - 0 i.e. AA <> Aa <> aa. Default.
' (Textual) - 1 i.e. AA = Aa = aa.
' All0OnlyOne1: Optional. Determines if all (0 - Default) or only
' the first (1) occurrence in each cell has to be found.
'*************************************************************************
Function CDSO(CountRange As Range, CountValue As Variant, _
Optional CountDelimiter As String = ",", _
Optional CompareBinary0Text1 As Long = 0, _
Optional All0OnlyOne1 As Long) As Long
Dim rng As Range ' Current Range (of Areas Collection)
Dim vntR As Variant ' Range Array (2D 1-based)
Dim vntC As Variant ' Cell Array (1D 0-based)
Dim vntCell As Variant ' Cell Variant
Dim i As Long ' Current Cell Row Counter
Dim j As Long ' Current Cell Column Counter
Dim k As Long ' CountRange Areas Counter
Dim m As Long ' Cell Array Element Counter
Dim ValCount As Long ' Value Counter
Dim strVal As String ' Value String
Dim strCell As String ' Cell String
' Convert CountValue to string (CStr), because arrays created
' using Split do only contain strings.
' Write CountValue to Value String.
strVal = CStr(CountValue)
' Loop through Areas Collection (ranges) of CountRange.
For k = 1 To CountRange.Areas.Count
' Check if Current Range contains one cell only.
If CountRange.Areas(k).Cells.Count = 1 Then
' Write value of Current Range (one cell only) to Cell Variant.
vntCell = CountRange.Areas(k)
' Go to Occurrences Counter Subroutine.
GoSub OccurrencesCounter
Else
' Copy Current Range to Range Array.
vntR = CountRange.Areas(k)
' Loop through rows of Range Array.
For i = 1 To UBound(vntR)
' Loop through columns of Range Array.
For j = 1 To UBound(vntR, 2)
' Write value of current element of Range Array to Cell
' Variant.
vntCell = vntR(i, j)
' Go to Occurrences Counter Subroutine.
GoSub OccurrencesCounter
Next
Next
End If
Next
' Write value of Value Counter to Count String Occurrences (CDSO).
CDSO = ValCount
Exit Function
' Occurrences Counter
' Purpose: Count the number of occurrences of CountValue in Cell String.
OccurrencesCounter:
' Check if Cell Variant is a number.
If IsNumeric(vntCell) Then Return
' Write value of Cell Variant converted to string to Cell String.
strCell = CStr(vntCell)
' Check if Cell String is not empty ("").
If strCell = "" Then Return
' Split Cell String by CountDelimiter into Cell Array.
vntC = Split(strCell, CountDelimiter)
' Loop through elements of Cell Array.
For m = 0 To UBound(vntC)
' Sometimes the values contain deliberate or accidental
' spaces, so Trim is used to remove them.
' If you want to use the vbTextCompare i.e. AA = Aa, AA = aa,
' in the formula set CompareBinary0Text1 to 1.
' Check if value of current element in Cell Array
' is equal to CountValue.
If StrComp(Trim(vntC(m)), strVal, CompareBinary0Text1) = 0 Then
' Count the occurrence i.e. increase Value Counter.
ValCount = ValCount + 1
' Note: If only the first occurrence in each cell is needed,
' increase efficiency with Exit For i.e. in the formula
' set All0OnlyOne1 to 1.
' Check if All0OnlyOne1 is equal to 1.
If All0OnlyOne1 = 1 Then
' Stop looping, occurrence found.
Exit For
End If
End If
Next
Return
End Function
'******************************************************************************
I need to Vlookup on column A of sheet1 with cell A1, A2 values
SD-121, SD-232, SD-23
SD-323,SD-333
and so on..
vLookup table in a different sheet with column A, B, C, D. Column A having
A B
SD-232 US
SD-23 UK
SD-323 IN
SD-333 SG
SD-121 CN
The lookup result is to be displayed in Column B of sheet1 result cell B1 and B2
B
CN, US, UK
IN, SG
You can create a user function to loop the values through the VLOOKUP function:
Function VLOOKUPARRAY(ByVal lookup_val As Range, ByVal table_array As Range, ByVal col_index_num As Integer, Optional ByVal range_lookup As Integer = 0) As String
Dim s As String
Dim a1() As String
Dim a2() As Variant
Dim i As Integer
'Recalculate whenever a calculation happens on the worksheet
Application.Volatile
'Get the lookup value from the cell
s = lookup_val.Value
'Split into array
a1 = Split(s, ",")
'Set output array to input array dimensions
ReDim a2(0 To UBound(a1))
'Loop through input array and set output array elements to lookup results using application lookup function.
For i = 0 To UBound(a1)
a2(i) = Application.WorksheetFunction.VLookup(Trim(a1(i)), table_array, col_index_num, range_lookup)
Next i
'Loop through output array and load values into a string
s = ""
For i = 0 To UBound(a2)
s = s & a2(i) & ", "
Next i
'Knock the final ", " off the end of the string
s = Left(s, Len(s) - Len(", "))
'Set function output to string value.
VLOOKUPARRAY = s
End Function
That was quick and dirty, but it gave me the desired output. Adjust as needed.
If you have Office 365 latest update from Feb 2016 then you can use the following array formula:
=TEXTJOIN(",",TRUE,IF(ISNUMBER(SEARCH(Sheet2!$A$1:$A$5,A1)),Sheet2!$B$1:$B$5,""))
being an array formula it must be confirmed with Ctrl-Shift-Enter instead of Enter when exiting Edit mode. If done correctly Excel will put {} around the formula.
The return will be in order of the list on sheet 2, not the order of the comma delimited string.
I've got an Excel spreadsheet with multiple columns of different lengths, each filled with unsorted numbers. Each column has a header.
Is there a way to determine which column(s) contain that number?
For instance, I'd love to be able to do =WHICHCOLS( 123, A, Z ) and have Excel tell me columns [B, C, and K] contain cells with a value of 123. Though, ideally, I'm after a comma separated list of headers.
Equally adequate would be to find all cells, in a range (or ranges), that have that value; e.g. [B19, C32, and K908].
I feel like I'm overlooking some obvious built-in function.
There might be an built-in way to do this but you can also write your own function.
Put this code in a VBA Module:
Public Function WHICHCOLS(searchValue As Double, srcRange As Range) As String
Dim rangeColumn As Range
Dim columnCell As Range
Dim headerRow As Long
headerRow = 1 ' HeaderInformation is in RowNr 1 '
WHICHCOLS = vbNullString
For Each rangeColumn In srcRange.Columns
For Each columnCell In rangeColumn.Cells
If columnCell = searchValue Then
If WHICHCOLS <> vbNullString Then WHICHCOLS = WHICHCOLS & ", "
WHICHCOLS = WHICHCOLS & srcRange.Parent.Cells(headerRow, columnCell.Column)
Exit For
End If
Next columnCell
Next rangeColumn
End Function
An example call in Excel would be:
=WHICHCOLS(7,A2:F3)