How can I add cell values based on column header? - excel

I am having trouble grasping how the operation I'm about to describe can be conceptualized, since I am new to coding.
A big spreadsheet includes 100 columns, and those need to be condensed down to 10 by adding together the columns. There is a key, so that all the columns tagged with "1" go to 1st new column, and so on.
Here is an example:
There are n original columns. Each one of those columns has a key (bottom left), and according to that key it must be added to column 1, 2, 3, or 4 of the new table (bottom right). This is all nice and clean but the real spreadsheet has perhaps 270+ columns and they must be condensed into 10 columns or so for 3000+ ID's where not all ID's have all columns filled.
I am not sure how to create that sort of loop, I thought of looping through the key first, then finding in the original columns each "A", adding them to first column of new table, then doing that through all of them, but I'm not sure how to avoid overwriting old sums with the new ones.
Cheers!

You can do it with SUMPRODUCT. Actually, you can code it on VBA using this same formula of SUMPRODUCT and pasting values or with Evaluate:
=SUMPRODUCT(--($A$2:$A$6=$F14)*$B$2:$M$6*TRANSPOSE(--($B$14:$B$25=G$13)))
Depending on your Excel version maybe you need to input the formula as array formula, so instead of normally, type the formula and press CTRL+ENTER+SHIFT
UPDATE: You can also do it with VBA but you need to make some changes to your source file to make it work with any dataset of any size:
Your data must be alone in a worksheet called DATA
Your keys must be alone in a worksheet called KEYS
The code will generate a new worksheet with the grouped data according to keys. It uses same formula than before, but does it everything alone.
Sub TEST()
Dim wk As Worksheet
Dim rngData As Range
Dim rngKeys As Range
Dim LR As Long 'last non blank row
Dim LC As Long 'last non blank column
Dim ThisKeys As Variant
Set wk = ThisWorkbook.Sheets.Add(, ThisWorkbook.Worksheets(ThisWorkbook.Worksheets.Count)) 'add new worksheet for output at end of workbook
With ThisWorkbook.Worksheets("DATA")
LR = .Range("A" & .Rows.Count).End(xlUp).Row
LC = .Cells(1, .Columns.Count).End(xlToLeft).Column
Set rngData = .Range(.Cells(2, 2), .Cells(LR, LC))
.Range("A2:A" & LR).Copy wk.Range("A2:A" & LR) 'copy names to output
End With
With ThisWorkbook.Worksheets("KEYS")
LR = .Range("A" & .Rows.Count).End(xlUp).Row
Set rngKeys = .Range("B2:B" & LR)
.Range("B2:B" & LR).Copy
wk.Range("B2").PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
End With
With wk
.Range("B2:B" & LR).RemoveDuplicates Columns:=1, Header:=xlNo
LR = .Range("B" & .Rows.Count).End(xlUp).Row
ThisKeys = .Range("B2:B" & LR).Value
.Range("B2:B" & LR).Clear
.Range("B1").Resize(1, UBound(ThisKeys)) = Application.WorksheetFunction.Transpose(ThisKeys) 'transpose keys to horizontal
.Range("A1").Value = "Names / Keys"
LR = .Range("A" & .Rows.Count).End(xlUp).Row
LC = .Cells(1, .Columns.Count).End(xlToLeft).Column
.Range("B2").FormulaArray = _
"=SUMPRODUCT(--(DATA!R2C1:R" & rngData.Rows.Count + 1 & "C1=RC1)*DATA!" & rngData.Address(True, True, xlR1C1) & "*TRANSPOSE(--(KEYS!R2C2:R" & rngKeys.Rows.Count + 1 & "C2=R1C)))"
.Range("B2").AutoFill Destination:=Range(.Range(.Cells(2, 2), .Cells(2, LC)).Address), Type:=xlFillDefault 'drag to right
.Range(.Cells(2, 2), .Cells(2, LC)).AutoFill Destination:=Range(.Range(.Cells(2, 2), .Cells(LR, LC)).Address), Type:=xlFillDefault 'drag to right
.Range(.Cells(2, 2), .Cells(LR, LC)).Value = .Range(.Cells(2, 2), .Cells(LR, LC)).Value 'paste as values, not formulas
End With
Erase ThisKeys
Set rngKeys = Nothing
Set rngData = Nothing
Set wk = Nothing
End Sub
I uploaded the file with the code so you can check it out: https://drive.google.com/file/d/1rc8oOPcqP4HBFEyamku24H9hHRFpncq_/view?usp=sharing

Related

Find duplicates in Column A and combine and separate all text values in column D then remove all duplicate rows

I have a script but it only takes the first value in column D then removes all duplicates. I'm sorry but I'm just learning VBA and need some help. Can this be adjusted or programmed differently to combine all the text values in column D separated by a colon?
To explain we are exporting ticket data and importing to a new tool but their import won't accept duplicates of ticket IDs (numbers, 1000-9000). Our thought is if we can combine the labor descriptions which contains our labor notes then we can retain the data we need and ignore the other info we do not need.
Sub foo()
Dim ws As Worksheet
Dim lstrow As Long
Set ws = Sheets("g1") ' Change to your sheet
With ws
lstrow = .Range("A" & .Rows.Count).End(xlUp).Row
With .Range("B4:M" & lstrow)
.Offset(, 26).FormulaR1C1 = "=IFERROR(INDEX(R4C[-26]:R" & lstrow & "C[-26],MATCH(1,INDEX((R4C1:R" & lstrow & "C1 = RC1)*(R4C[-26]:R" & lstrow & "C[-26] <>""""),),0)),"""")"
ws.Calculate
.Value = .Offset(, 26).Value
.Offset(, 26).ClearContents
End With
With .Range("A4:M" & lstrow)
.Value = .Value
.RemoveDuplicates 1, xlGuess
End With
End With
End Sub
This definitely removes all duplicates but only keeps the first cell value it finds in the secondary column.

How to copy ranges from several sheets to one sheet

I want to copy ranges from several worksheets to one sheet.
The range to copy is C3 to the last row of data.
I need to paste it into a column on the main sheet in B6, then repeat the process on the next sheet (from C3 again) into the next column C6 and so on to column J.
I tried:
Set WkSh = ActiveSheet
Set DatShs = Sheets(Array("E0303_0", "E0304", "E0305", "E0306", "E0307", "E0308", "E0309", "E0310", "E0311_0"))
Set DatSh = Sheets(DatSh) 'I get Run time Error '13' Type mismatch here
Set Lrow = DatSh.Cells(Rows.Count, "C").End(xlUp)
TnD = DatSh.Range("C:B").Find("*", , , , xlByRows, xlPrevious).Row
Set RngGrp = DatSh.Range("TnD", Lrow)
Sheets("E0303_0").Range(RngGrp).Copy
ActiveWorkbook.WkSh.Range("A6").Paste
ActiveWorkbook.Sheets("E0304").Range("C3" & Lrow).Copy
ActiveWorkbook.WkSh.Range("C6").Paste
ActiveWorkbook.Sheets("E0305").Range("C3" & Lrow).Copy
ActiveWorkbook.WkSh.Range("D6").Paste
ActiveWorkbook.Sheets("E0306").Range("C3" & Lrow).Copy
ActiveWorkbook.WkSh.Range("E6").Paste
ActiveWorkbook.Sheets("E0307").Range("C3" & Lrow).Copy
ActiveWorkbook.WkSh.Range("F6").Paste
ActiveWorkbook.Sheets("E0308").Range("C3" & Lrow).Copy
ActiveWorkbook.WkSh.Range("G6").Paste
ActiveWorkbook.Sheets("E0309").Range("C3" & Lrow).Copy
ActiveWorkbook.WkSh.Range("H6").Paste
ActiveWorkbook.Sheets("E0310").Range("C3" & Lrow).Copy
ActiveWorkbook.WkSh.Range("I6").Paste
ActiveWorkbook.Sheets("E0311_0").Range("C3" & Lrow).Copy
ActiveWorkbook.WkSh.Range("J6").Paste
Your code makes no sense after the first 2 lines. You are trying to set a sheet to itself Set DatSh. What you want to do is loop through the array. Your lastrow is not a row number, but a range and you are trying to add to a cell. The following is the logic you want to use, you can modify as needed.
Sub test()
Dim SheetArray As Variant
Set SheetArray = Sheets(Array("E0303_0", "E0304", "E0305", "E0306", "E0307", "E0308", "E0309", "E0310", "E0311_0"))
For i = 1 To SheetArray.Count
LR = Sheets(i).Cells(Rows.Count, 3).End(xlUp).Row
Sheets(i).Range(Sheets(i).Cells(3, 3), Sheets(i).Cells(LR, 3)).Copy
ActiveSheet.Cells(i, 6).Paste
Next i
End Sub

The search block throws 'Run-time error 91'

Good day,
When I search within a column for a particular string, I get the 91 run error.
I have tried to modify the logic - copied the result to the new sheet and then, did the deletion - see the second piece of code.
Then, I found that it happens because the vba cannot find the text, so I asked a person who runs this report in the requested language to change "??????? ATLAS ????-???" to the way it is written in the data source language. But it didn't help.
Columns("A:A").Select
Selection.Find("??????? ATLAS ????-???", LookIn:=xlValues).Select
Range(ActiveCell.Address & ":" & Cells(Cells(Rows.Count, "A").End(xlUp).Row, ActiveCell.Column + 4).Address).Select
Selection.Copy
'Pasting the Ylan-Yde data to the new sheet
Sheets("interim").Select
Range("A1").Select
ActiveSheet.Paste
'Copying the Ylan-Yde data to a new sheet
Cells.Select
Selection.Copy
Sheets("interim").Select
Cells.Select
ActiveSheet.Paste
Columns("A:A").Select
Selection.Find("??????? ATLAS ????-???", LookIn:=xlValues).Select
Range("A1:A" & ActiveCell.Row - 1).EntireRow.Delete```
If I stick to the 2nd version, the code is supposed to find the a certain string in a column (it is present only once) and delete all rows before the row with the found string.
You should avoid using .Selectbecause everything can go wrong if the user interacts with excel. Instead you need to Declare variables for your workbooks and worksheets.
This approach is assuming your word only appears once and its Length is always 22, so the other ATLAS that shows on the column don't have the same length. With that in mind this should work:
Option Explicit
Sub Test()
Dim arr As Variant, LastRow As Long, ws As Worksheet, wsInterim As Worksheet, i As Long, StartRow As Long, NowDelete As Boolean
With ThisWorkbook
Set ws = .Sheets("NameWhereAtlasIs")
Set wsInterim = .Sheets("Interim")
End With
With ws
LastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
arr = .Range("A1:A" & LastRow).Value 'store the column A inside an array
End With
NowDelete = False
For i = 1 To UBound(arr) 'find the ATLAS with the exact length match.
If arr(i, 1) Like "*ATLAS*" And Len(arr(i, 1)) = 22 Then
StartRow = i
NowDelete = True
arr(i, 1) = vbNullString 'if you intend to delete this row too, the one with the ATLAS
End If
If NowDelete Then arr(i, 1) = vbNullString 'from the moment you find your match all the rows will be emptied on column A
Next i
ws.Range("A" & StartRow & ":A" & LastRow).Copy wsInterim.Range("A1")
ws.Range("A1:A" & LastRow).Value = arr 'this would paste the array back with the empty cells you cleared before
End Sub

Paste (dynamic) lookup formula in dynamic number of columns

I have a sheet "2018" and "2019" which i created previously. 2019 only differs from 2018 in that it may have some rows added and/or some deleted.
Form Cell "A3" and downwards i have skills listed and a "X" in the columns after if the person the column belongs to has this skill.
Now i need to fill the columns of 2019 with known X with formula below, first a bit of context code for the range selection part:
Dim rng As Range
Dim rngbegin As Range
Dim rngend As Range
Dim newrng As Range
Sheets("2018").Activate
Set rng = Application.InputBox '...and rest of the code
rng.Copy
Sheets("2019").Range("B:B").Insert Shift:=xlToRight
Sheets(2019).Activate
Set rngbegin = rng.Cells(3, 1)
Set rngend = rng.Cells(3000, rng.Columns.Count)
Set newrng = Range(rngbegin.Address & ":" & rngend.Address)
newrng.ClearContents 'To clear everything in the difined range but the headder rows
Here is a formula i could use if the columns wouldn't be varying.
Range("B3").Select
ActiveCell.Formula = "=IFERROR(LOOKUP(2,1/($A3='2018'!$A$3:D$5000),'2018'!$B$3:$B$5000),"")"
Range("B2").AutoFill Destination:=Range("B2:B" & Range("A" & Rows.Count).End(xlUp).Row)
The Formula works but i have the following problems:
1 - Required) I can't hardcode the formula for every column because the number of colums may change. (I store the number of columns as range var selected from the user via application.inputbox - that's how i inserted the colums in the new 2019 sheet)
2 - optional) I hardcoded the rows to a much higher number than are used because i didn't thnk of counting Column A and then use the range.count.Address(?) as end of the search vector. Just came into my mind lol
You will probably need to tweak a few addresses. I left much of your code unchanged so you can easily adapt what I have came up with for your purposes.
Sub Whatever()
With Sheets("2018")
' Get the address of the old range, not used later in the macro
iRows = .Cells(Rows.Count, 1).End(xlUp).Row
iCols = .Cells(3, Columns.Count).End(xlToLeft).Column
Set rngOld = Range(.Cells(3, 2), .Cells(iRows, iCols))
End With
With Sheets("2019")
' Get the address of the new range
iRows = .Cells(Rows.Count, 1).End(xlUp).Row
iCols = .Cells(2, Columns.Count).End(xlToLeft).Column
Set rngNew = Range(.Cells(3, 2), .Cells(iRows, iCols))
'Clear the new range
rngNew.Clear
' Populate the formula
' Not very elegant, VBA solution would probably look nicer
.Range("B3").Formula = "=IFERROR(if(LOOKUP(2,1/('2018'!$A$3:$A$" & iRows & " =$A3),'2018'!B$3:B$" & iRows & ")=""X"",""X"",""""),"""")"
'Fill the formula
Set rngTemp = .Range(.Cells(3, 2), .Cells(3, iCols))
rngTemp.FillRight
Set rngTemp = .Range(.Cells(3, 2), .Cells(iRows, iCols))
rngTemp.FillDown
End With
End Sub

How to Paste a Range onto another worksheet with filters on

This seems like a simple task but I keep running into various errors. I need to filter worksheet B and then copy a column of data. I then need to filter worksheet A and then paste the copied data into a column.
Worksheets("SheetB").Select
lastRowOne = Range("B" & Rows.Count).End(xlUp).Row
Range("DL2:DL" & lastRowOne).AutoFilter Field:=116, Criteria1:="<>Apples"
lastRowTwo = Range("B" & Rows.Count).End(xlUp).Row
Range("DG2:DG" & lastRowTwo).AutoFilter Field:=111, Criteria1:=Target
'Target is already defined earlier in the Macro and functions fine
lastRowThree = Range("B" & Rows.Count).End(xlUp).Row
Range("DX2:DX" & lastRowThree).Copy
Worksheets("SheetA").Activate
lastRowFour = Range("B" & Rows.Count).End(xlUp).Row
Range("A2:A" & lastRowFour).AutoFilter Field:=1, Criteria1:=Target
lastRowFive = Range("B" & Rows.Count).End(xlUp).Row
Range("Z2:Z" & lastRowFive).SpecialCells(xlCellTypeVisible).Select
Selection.PasteSpecial Paste:=xlPasteRange, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
In place of the last line I have also tried:
ActiveSheet.Paste
The first returns a "Run-time error '1004':
PasteSpecial method of range class failed
the ActiveSheet.Paste returns a "Run-time error '1004':
Paste method of Worksheet class failed
Although this code is not the cleanest, it all functions with the exception of the "pasting" onto 'sheetA' in Column Z. I also need the data pasted into AA if that can be included in a fix.
Thanks !
Here's (I hope) the same macro, but without .Select/.Activate, and a little tweaking. For instance, you don't need more than one "lastRow" variable. Since you really just reset it, you can use one.
Sub tester()
' First create, then SET, worksheet variables to hold the sheets. We use these when
' referring to ranges, cells, etc.
Dim aWS As Worksheet, bWS As Worksheet
Set aWS = Worksheets("SheetA")
Set bWS = Worksheets("SheetB")
Dim lastRow As Long 'AFAICT, you only need this one Last Row variable. Just update it each time.
Dim copyRng As Range
With wsB ' working with SheetA
lastRow = .Cells(.Rows.Count, 2).End(xlUp).Row
.Range("DL2:DL" & lrOne).AutoFilter Field:=116, Criteria1:="<>Apples"
lastRow = .Cells(.Rows.Count, 2).End(xlUp).Row
.Range("DG2:DG" & lastRow).AutoFilter Field:=111, Criteria1:=Target
lastRow = .Cells(.Rows.Count, 2).End(xlUp).Row
' We now SET the range we want to copy. We can avoid copy/paste by setting two ranges equal
' to eachother. For now, let's store the COPY RANGE in a Range variable
Set copyRng = .Range("DX2:DX" & lastRow).SpecialCells(xlCellTypeVisible)
End With 'bWS
Dim pasteRng As Range
With aWS
lastRow = .Cells(.Rows.Count, 2).End(xlUp).Row
.Range("A2:A" & lastRow).AutoFilter Field:=1, Criteria1:=Target
lastRow = .Cells(.Rows.Count, 2).End(xlUp).Row
Set pasteRng = .Range("Z2:Z" & lastRow).SpecialCells(xlCellTypeVisible)
End With 'aWS
pasteRng.Value = copyRng.Value
End Sub
The only hesitation I have is the pasting to SpecialCells. AFAIK, if the paste range is different than the copy range, you might get some errors. In any case, try the above and let me know what happens.
An important thing to pay attention to, especially when using multiple worksheets, is that you should be explicit with which sheet you want to get a Range(),Cells(),Rows(),Columns(),etc. Otherwise, it's going to get that info. from the ActiveSheet, whatever that may be.

Resources