I'd like to combine cells in the right column into one cell according to the adjacent cell on the left. I tried Merging but I could only get so far. And after searching online I couldn't find anything that can parse each row and combine for the length of the left cell's span. I know it's a CONCATENATE function, but how would I get it to parse the whole spreadsheet?
This is an example of the results I would want for the above:
This may be too complicated - in which case I would go back to the drawing board and do a full VBA version, but initially I was looking for a challenge to construct a solution only using formulas. Unfortunately, there appears to be no standard formula-based approach to concatenate a variable number of cells.
So, to accomplish this, I added one function:
Function CombineRange(ByRef rng As Range, ByVal delim As String)
Dim arr
Dim i As Long
arr = rng.Value
CombineRange = ""
For i = 1 To UBound(arr)
If i > 1 Then
CombineRange = CombineRange & delim
End If
CombineRange = CombineRange & arr(i, 1)
Next i
End Function
Assumptions:
your data is in a sheet called "YourData"
Your merged data is column A
Your "single row" data is column B
Row 1 is some kind of header row.
Next, set up four columns on a new sheet (I call it "Collapsed")
A - Start Row = (first row) whatever row your data starts on (2, in our case)
A - Start Row = (all others) A2+B2
B - Offset = {IFERROR(MATCH(FALSE,ISBLANK(INDIRECT(ADDRESS(A2+1,1,,,"YourData")&":A200")),0),0)}
Note this is an array function, so you need to do shift+Enter when entering it
C - Level1 = =INDEX(YourData!A:A,A2)
D - Combined Level 2 = =IF(B2<=1, INDIRECT(ADDRESS(A2,2,,,"YourData")), CombineRange(INDIRECT(ADDRESS(A2,2,,,"YourData")&":"&ADDRESS(A2+B2-1,2)),"; "))
Related
I'm trying to search on the specific column(E), and if matched with the first 4 digit, I would like to copy the number to a different column.
Column E is where i would like to paste all the random number(dynamic)
Column A/B/C is static where i would add 4 digits from time to time.
Column I/J/K is where is would like to paste the result.
PS:
I'm doing it manually and would really appreciate if someone can help me out with the automation hence no code is provided. :(
Having ExcelO365 means you may use FILTER(). Therefor try the below:
Formula in I2:
=FILTER($E:$E,ISNUMBER(MATCH(--LEFT($E:$E,4),A:A,0)))
Drag right to K2. Now, this is dynamic and will change accordingly upon data entry in column E:E, or changing values in A:C.
this is the code to execute on sheet 1, it goes through the entire column E and validates through the formula of counting if in each of the first three columns and assigns the value found in the corresponding columns.
Sub macro()
Dim Static_Data As String
Dim Sht As Worksheet
Set Sht = ThisWorkbook.Sheets("Hoja1")
Active_row = 2
Do While Sht.Range("E" & Active_row).Value <> ""
Static_Data = Sht.Range("E" & Active_row).Value
For i = 1 To 3
If Application.WorksheetFunction.CountIf(Sht.Columns(i), Mid(Static_Data, 1, 4)) > 0 Then
Sht.Cells(Sht.Cells(Rows.Count, i + 8).End(xlUp).Row + 1, i + 8).Value = Static_Data
End If
Next i
Active_row = Active_row + 1
Loop
End Sub
For Excel versions that don't support FILTER or as an alternative you can use standard formulas for this.
If you use columns F-H as helper columns (and these columns can be hidden) then the formula in F2 will be:
=IF(NOT(ISERROR(VLOOKUP(VALUE(LEFT($E2,4)),A$2:A$100,1,FALSE)))=TRUE,$E2,"")
The formula can then be copied across and down. This will find your matches.
In order to then remove the blanks from the data you can use the following formula in I2 and again copy across and down. Depending on how many numbers you want to add in, you may want to extend the range A$2:A$100 in the top formula and F$2:F$100 in the bottom formula
=IFERROR(INDEX(F$2:F$100,AGGREGATE(15,6,(ROW(F$2:F$100)-ROW(F$2)+1)/(F$2:F$100<>""),ROWS(I$2:I2))),"")
As you can see in the image that "Col B" has same number multiple times. For ex: "1" is four times, "2" is three times, and so on. However, all these numbers correspond to a specific number from "Col A". What I am trying to do is get the the column I have highlighted in orange and yellow. You can clearly see what I have done. What I need is a excel function that does it for me. This is just a sample. I have dataset with million data points, and I can't type all that.
Thanks!!
Formula for cell E2
=IFERROR(SMALL(IF($B:$B=$D2,ROW($B:$B)-1),COLUMN(A:A)),"")
Entered as an Array Formula (Enter with Ctrl-Shift-Enter rather than just Enter)
Copy accross for as many cells as you wish
Note: this formula is quite slow. A well designed UDF will be faster.
One way to solve this with a UDF is
Function MultiLookup(Val As Variant, rItems As Range, rLookup As Range, Index As Long) As Variant
Dim vItems As Variant
Dim i As Long, n As Long
With rItems
If IsEmpty(.Cells(.Count)) Then
Set rItems = Range(.Cells(1, 1), .Cells(.Count).End(xlUp))
End If
End With
vItems = rItems
n = 0
For i = 1 To UBound(vItems, 1)
If vItems(i, 1) = Val Then
n = n + 1
If n = Index Then
MultiLookup = rLookup.Cells(i, 1)
Exit Function
End If
End If
Next
MultiLookup = vbNullString
End Function
Use like this, for cell E2
=MultiLookup($D2,$B:$B,$A:$A,COLUMN(A:A))
Again, copy accross for as many cells as use wish
Here is another non UDF solution, that works fast. Please note that it uses the AGGREGATE-Function, which is only available since Excel-2010.
Put this in E2 and drag across.
=INDEX($A$2:$A$1000,AGGREGATE(15,6,Row($1:$1000)/($B$2:$B$1000=$D2),COLUMN(A:A)))
You can also wrap this formula with a IFERROR to make sure you don't get the #VALUE! Error.
A PivotTable, with it's body copied into D2 and then blanks removed should be quick:
I am dealing with a lot of GIS metadata that I am importing into Excel, with many rows and columns of blank or zero values. I am trying to take data like this:
(Left Column is the name, columns to the right are the values associated with that name)
and by only selecting the columns that have a value, end up with a new set of tables showing the nonzero rows and its corresponding name to the left:
I have tried doing it by filtering the data in a table so that it only shows nonzero values, copying that column and the far left column and pasting it onto a new sheet. This is easy if I only have a few columns, but given the amount I have it is very painstaking. I have to filter each column separately because the rows in each column may or may not have blanks or zeros depending on the column.
Can the LOOKUP function be used for this, or would using VBA be better?
why can't use 2 simple loops (rows, columns)??
i suppose that the data start from "A1", I count rows and columns and play this code.
Sub tras()
Dim lastRw As Integer
Dim lastCol As Integer
Dim ResultRow As Integer
ResultRow = 20 '1th row for result
lastRw = Range("A1").End(xlDown).Row
lastCol = Cells(1, ActiveSheet.Columns.Count).End(xlToLeft).Column
Set rgn = Range("A1", Cells(lastRw, lastRw))
For x = 2 To lastCol
For y = 1 To lastRw
If Cells(y, x) <> "" Then
Cells(ResultRow, 1) = Cells(y, 1).Value
Cells(ResultRow, 2) = Cells(y, x).Value
ResultRow = ResultRow + 1
End If
Next y
ResultRow = ResultRow + 1
Next x
End Sub
A filtering in a similar case can be performed with array formulas such as in the example below.
See an example:
http://i.stack.imgur.com/YlyUN.jpg
(I cannot post images yet - please edit my post so that the image is integrated)
To use it select range E1:E6, write the shown formula and press ctrl+shift+enter.
The logic is as follows:
1) based on range B1:B6 create two vectors: 1 with row numbers of each row which simply is a v1 = {1,2,3,4,5,6} in this case and a vector of TRUEs and FALSEs for each row with TRUE if a row is nonempty and FALSe if empty: in this case it is v2 = {TRUE,FALSE,TRUE,TRUE,FALSE,FALSE}.
2) multiply v1 and v2 element wise which gives us vector v3 = {1,0,3,4,0,0}
3) using SMALL function extract values from the smallest to the largest using COUNTBLANK function to skip all the zeros (their number is equal to number of blanks) - ROW function used on range E1:E6 serves as our iterator in SMALL function
4) after point 3) you end up with row numbers of non-empty cells based on range B1:B6, now you have to call INDEX function to extract values from range A1:A6
5) add IFERROR on top of everything so that it returns "" when going out of SMALL's input array's range
To assign values in column F simply use INDEX + MATCH (I encourage everyone to forget about *LOOKUP).
What you end up here with is something that I think addresses your core problem. If you want to make it all "draggable" or "fillable", use it on ranges other that beginning with 1 or put it all in setup row after row rather than column after column, you'll have to make some modifications to the formulas used here but the logic will be the same.
Is there a way to concatenate multiple columns if the a row is duplicate? I have a spreadsheet where column A has duplicate team but there area and LD (column b and c) are different value. I would like to have a formulate at column E where it will concatenate column B and C with dash and append next row values. See the attached picture highlighted row E. Any idea how to do this with excel formula or may be VBA. I tried this formula in column E =IF(A3=A4,D3&";"&D4) but it returns false for the last duplicate row.
This is not possible with formulas. It requires a VBA-based solution.
I wrote a custom routine for you. Please place this in a standard code module:
Public Sub ConcatTeamZones()
Const SOURCE = "A1"
Const OUTPUT = "E1"
Dim i&, j&, s$, v, w
v = Range(SOURCE).CurrentRegion
ReDim w(1 To UBound(v), 0)
For i = 2 To UBound(w)
If v(i, 1) <> v(i - 1, 1) Then
w(i - 1, 0) = s
s = s & v(i, 2) & "-" & v(i, 3)
s = ""
Else
s = s & ";"
End If
s = s & v(i, 2) & "-" & v(i, 3)
Next
w(i - 1, 0) = s
Range(OUTPUT).Resize(UBound(w)) = w
End Sub
And then from the worksheet with your team data, press ALT-F8 to bring up the Macro Dialog. Run the ConcatTeamZones macro.
Note 1: this assumes that column A is sorted.
Note 2: You can edit the first two lines to specify which columns contains the source (team data) and which column you wish the output.
It can be done using formulas, it’s just a matter of perspective:
Assuming data is sorted by Team
This formula gives the concatenated result with the maximum of combinations on top. Enter this formula in cell E2 and copy till last record.
=CONCATENATE($D2,IF(EXACT($A2,$A3),";"&$E3,""))
To assign the max possible combinations to each Team enter this formula in F2 and copy till last record.
=INDEX($E:$E,MATCH($A2,$A:$A,0),0)
Here's how I would do it...
Cell "A1": =COUNTIF(B$2:B2,B2)&B2 - This is to create a unique key. Copy down the length of your table
Then I would use an advanced query (with vba maybe) to create a list of unique values for team in the "F" column
Cell "G2": =VLOOKUP("1"&F2,A:D,3,0)&"-"&VLOOKUP("1"&F2,A:D,4,0)&IF(ISERROR(VLOOKUP("2"&F2,A:D,3,0)),"",", "&VLOOKUP("2"&F2,A:D,3,0)&"-"&VLOOKUP("2"&F2,A:D,4,0))&IF(ISERROR(VLOOKUP("3"&F2,A:D,3,0)),"",", "&VLOOKUP("3"&F2,A:D,3,0)&"-"&VLOOKUP("3"&F2,A:D,4,0))&IF(ISERROR(VLOOKUP("4"&F2,A:D,3,0)),"",", "&VLOOKUP("4"&F2,A:D,3,0)&"-"&VLOOKUP("4"&F2,A:D,4,0))
This function creates your combined references. It would be longer if you expected more than 4 occurrences of teams.
Just copy "IF(ISERROR(VLOOKUP("4"&F2,A:D,3,0)),"",", "&VLOOKUP("4"&F2,A:D,3,0)&"-"&VLOOKUP("4"&F2,A:D,4,0))" and change the "4" to "5" etc
You could hide column A (to tidy up).
Sorry, I tried to include an image but insufficient reputation :-)
I would like to highlight duplicate rows in Excel VBA. Assume I have the following exemplary table with columns A, B, C and D for testing:
A B C D (Strings)
1 1 1 dsf
2 3 5 dgdgdgdg
1 1 1 dsf
2 2 2 xxx
6 3 4 adsdadad
2 2 2 xxx
The duplicate rows should be highlighted in any colour, e.g. grey. I am looking ideally for fast performing code, as it will be used for rather big tables.
Note there are solutions available for highlighting duplicate cells (but not duplicate rows). I don't know how to identify if rows are duplicates and at the same time how to do that fast, i.e. without nested looping. The solution should be in VBA (not Excel).
What is the best/fastest way to achieve that?
add a conditional formatting with the following sumproduct formula (or a countifs)
=SUMPRODUCT(($A$1:$A$6&$B$1:$B$6&$C$1:$C$6=$A1&$B1&$C1)*1)>1
Explanation:
SUMPRODUCT is handy to work with ranges which you need to manipulate prior to checking a condition. In this case I concatenate A, B & C columns across the range and compare it with the concatenation of the current row. I then convert the TRUE/FALSE array to a 1/0 array by multiplying by 1 and the SUM part of SUMPRODUCT sums the rows where the condition is true, giving me the duplicate rows (all occurences). If you have a small range, using the formula evaluation you can clearly see how this works.
It's a quick fix, but performance is not ideal, I use it a lot for detecting duplicates or generating sequential numbers.
Solution from comments suggested by ponydeer - higher performance
based on sorting suggesting, requires to add key column, put in auto filters and sort on key, then do conditional on key column:
I have tested 3 different approaches on the sample file link from OP's comment. Probably the VBA implementations were not optimal, but below are the results with average time of 100 passes:
1) Conditional formatting using:
a)SUMPRODUCT concatenating columns - 3s
b) COUNTIFS with full column reference - 1.9s
c) COUNTIFS referencing used ranges - 0.2s
2) Sorting the range on all columns, comparing row by row, sorting back - 0.3s
3) Using advanced filter 3.5s
Here is the code for the fastest method:
Sub CF1()
Application.ScreenUpdating = False
Dim sFormula As String
Dim rRng As Range
Dim nCol As Integer, i As Integer
Set rRng = Range("A1").CurrentRegion
nCol = rRng.Columns.Count
'build the formula
sFormula = "=COUNTIFS("
For i = 1 To nCol
sFormula = sFormula & rRng.Columns(i).Address & "," & _
rRng.Cells(1, i).Address(False, True)
If i < nCol Then sFormula = sFormula & ","
Next
sFormula = sFormula & ")>1"
'write the formula in helper cell to get it's local version
rRng.Cells(1, nCol + 1).Formula = sFormula
rRng.FormatConditions.Delete
With rRng.FormatConditions.Add(Type:=xlExpression, _
Formula1:=rRng.Cells(1, nCol + 1).FormulaLocal)
.Interior.ThemeColor = xlThemeColorAccent3
End With
rRng.Cells(1, nCol + 1).Clear
Application.ScreenUpdating = True
End Sub
Sort your range first regarding all columns
Workbooks(1).Sheets(1).Range("A:C").Sort Key1:=Workbooks(1).Sheets(1).Range("A:A"), Order1:=xlAscending, Key2:=Workbooks(1).Sheets(1).Range("B:B"), Order2:=xlAscending, Key3:=Workbooks(1).Sheets(1).Range("C:C"), Order3:=xlAscending, Orientation:=xlSortRows
Then loop through all rows and compare them with the one above them
Dim a As Application
Set a = Application
For i=1 to 1000 ' here you need to set the number of rows you have
if Join(a.Transpose(a.Transpose(ActiveSheet.Rows(i).Value)), Chr(0)) = _
Join(a.Transpose(a.Transpose(Sheets(1).Rows(i+1).Value)), Chr(0)) then
Sheets(1).Range(i+1 & ":" & i+1).EntireRow.Interior.Color = 49407
end if
Next i
The comparison of two rows is based on this thread: How to compare two entire rows in a sheet
Please insert the names of your Workbook, Sheet and set your range and the limits in the code yourself.
I think fastest/best will depend upon the proportion of duplicates – only one row should be quicker than 50% as in the example – and on the actual size of the array (how many columns from which to create a key, etc).
Given that it is rarely possible to beat inbuilt functions with ‘pure’ VBA I suspect using the UI, within VBA if desired, will be faster in some circumstances. Eg:
Add an index column (series fill would serve), copy entire sheet (say to Sheet2), apply Remove Duplicates to all but index column, then apply as CF formula rule of this kind the relevant range of the original sheet:
=$A1=MATCH($A1,Sheet2!$A$1:$A$3000,0)>0
Assuming the start point is like so:
and a ColumnA inserted with numeric series fill starting 1, Sheet2 should look so after Remove Duplicates:
I have assumed ColumnE is to be ignored as far as duplication is concerned.
In source sheet, select array (from A1: - see!), eg A1:I6 and HOME >Styles - Conditional Formatting, New Rule..., Use a formula to determine which cells to format, Format values where this formula is true::
=$A1=MATCH($A1,Sheet2!$A:$A,0)>0
Format..., Fill, grey, OK, OK.
For me results in:
Sub HighlightDuplicateRows_2()
'This one is more modifiable and can handle multiple columns of data
'Just add another *WorksheetFunction.CountIf(Range("A2:A" & LastRow),Cells(r,1).Value) > 1* and change the column values
Dim LastRow As Long
LastRow = Cells(Rows.Count, "A").End(xlUp).Row
For r = 2 To LastRow
If WorksheetFunction.CountIf(Range("A2:A" & LastRow), Cells(r, 1).Value) > 1 And WorksheetFunction.CountIf(Range("B2:B" & LastRow), Cells(r, 2).Value) > 1 Then
Rows(r).Interior.ColorIndex = 6
End If
Next r
End Sub