So I'm neck deep in this macro that I've created. My peers are requesting more on outputs than I had originally displayed. I need a macro that can do the following. So the question I have could normally be solved with VLOOKUP. However, in this case it is very important to know if there are really two answers and what both would be, making VLOOKUP obsolete. So lets say I have 2 columns. One showing Run # and the other showing Vertical, Curve, or Lateral (they progress down the column in that order). lets say Run #1 is Vertical. Run #2 began vertical, but finished curved. Run #3 is curve, but finished lateral. Finally, run #4 is lateral.Example:
Run# Section
1 Vertical
1 Vertical
1 Vertical
2 Vertical
2 Vertical
2 Curve
2 Curve
3 Curve
3 Curve
3 Lateral
3 Lateral
4 Lateral
4 Lateral
4 Lateral
4 Lateral
What I need is this below. Basically before I would preform this, I would be using a pivot table with Run# in "Rows" and then collecting other data in it as well. I then will copy and paste the value's only to a different sheet where then I will need to add in the values in "section" as shown below.
Run# Section
1 Vertical
2 Vertical & Curve
3 Curve & Lateral
4 Lateral
Thanks for taking a look at this guys. I've tried finding something on this, but nothing shows multiple results in the same cell. Any help is greatly appreciated! Have a great day!
I have a udf called JOINDISTINCT (code below) - you can use this in conjunction with an array formula to do what you need.
For the formula, assuming that your run data is in columns A and B and that column E has the distinct run list, in F2 have the array formula (entered with Ctrl+Shift+Enter)
=JOINDISTINCT(IF($A$1:$A$16=E2,$B$1:$B$16,"")," & ")
Note that we don't use the whole column, just the populated range, in order to speed up execution. The array part checks column A values to be matching the run number in E2 and then will return the Section or blank. The JOINDISINCT takes that array and returns the distinct list, separated by (in this case) " & "
VBA Code for the function
Public Function JOINDISTINCT(rng As Variant, Optional separator = ", ") As String
JOINDISTINCT = ""
Dim v As String
'Dictionary needs REFERENCE: Microsoft Scripting Runtime
Dim dict As New Dictionary
For Each c In rng
If IsObject(c) Then
v = c.Text
Else
v = c
End If
If v <> "" Then
If Not (dict.Exists(v)) Then
dict.Add v, 1
If JOINDISTINCT = "" Then
JOINDISTINCT = v
Else
JOINDISTINCT = JOINDISTINCT & separator & v
End If
End If
End If
Next c
Set dict = Nothing
End Function
Related
My research shows that I need to use Visual Basic. I am a programmer/developer, but have never used VB so if anyone could dumb it down it would be appreciated.
Here's my working excel function:
=IF(MATCH(1,E1:DP1,0),D1,FALSE)
I want to loop a few of those numbers such that:
=IF(MATCH(141,E1:DP378,0),D378,FALSE)
THEN take my answers (which will be strings, because column D are all strings, the rest of the excel file are numbers)
=CONCAT
end goal: have 141 String arrays populated based on the data in my table.
I went ahead and made my first attempt at VBA like this:
Sub myFunc()
'Initialize Variables
Dim strings As Range, nums As Integer, answer() As Variant, listAnswers() As Variant
'set variables
strings = ("C1:C378")
nums = 141
i = 0
j = 0
ReDim Preserve answer(i)
ReDim Preserve listAnswers(j)
'answer() = {""}
'for each in nums
For counter = 0 To nums
ReDim Preserve listAnswers(0 To j)
'set each list of answers
listAnswers(i) = Join(answer(), "insertJSONcode")
j = j + 1
'for each in Stings
For Each cell In strings
If cell <> "" Then
ReDim Preserve answer(0 To i)
answer(i) = 'essentially this: (MATCH(2,E1:DP1,0),D1,FALSE)
i = i + 1
end If
next cell 'end embedded forEach
Next LCounter 'end for loop
'is this possible? or wrong syntax?
Range("A:A").Value = listAnswers() ' should print 141 arrays from A1 to A141
End Sub
EDIT:
Important note I do NOT need to call the sheet by Name. I've successfully written integer values to by excel sheet in column A without doing so.
Also, the VBA I wrote I was never intended to work, I know it's broken at least where answer(i) is supposed to write to something. I'm only putting that code there to show I was able to at least able to get into spitting distance of the proper logic and prove I've put some serious effort into solving the problem and give a rough starting point.
Here's an image of the excel format. Column C goes down to 378 and the numbers listed from E through DP are populated by a database. It consists of blank cells and numbers between 1 and 141.
Looking back at my if statement:
=IF(MATCH(2,E2:DP2,0),D2,FALSE)
If I were to type that exactly into cell B2 it would output the correct answer "text2". which is neat and all, but I need every instance of text 2 written out, then CONCAT those results. Easy so far, I could drag that down all the way through column B and have all of my "text" strings in one column, CONCAT that column and there's the answer. However I don't just need #2, I need each number between 1 and 141. Plus I want to avoid writing 141 columns with a CONCAT on top of each one.
I have an excel file which contain information of the composite in 1 row and in below rows information of the components of this composite. Number of component rows below a composite are varied between 2 - 20 and there can be many composites in a file.
My question is: is it possible somehow to define how many rows are in the components and to extract information from each component in to one cell(concatenate). Problem I face is that number of rows are different each time and there can be multiple composites in the file containing components. So i do not know how to stop my loop and start a new composite aggregation.
Maybe there are ways to loop from Request1(ColumnA) and assign "Request1" as a text to every empty column below until it reaches Request2, after that is finished to concatenate based on Request"n"
Example what i want the data to look like
~~~~~~~~~~~~EDIT~~~~~~~~~~~~~~~~~~~~
I might have over complicated my question
I was just looking to concatenate information from different set of rows(for simplicity just 1 consistent cell from every row) in to 1 cell(for example last cell in the first column) for each specific composite(which contains components) My problem is I do not know how to stop the concatenation and start a new one when i am working with a new composite(new set of rows).
So as an example from the first picture, I would like to have "Request 1 Green Yellow White" (cells: A1, F1, F2,F3) populated in cell J1, and "Request 2 Amber Red White Blue" (cells: A4,F4,F5,F6,F7)populated i cell J4
#######EDIT
I have established another way of doing but still struggle with concatenation formula.
In this picture example
https://i.stack.imgur.com/iQdNu.jpg
If my table stars from row 2
=IF(A2="",J1,A2) - by putting this in column J and dragging down i will get his Request 1
Request 1
Request 1
Request 2
Request 2
Request 2
Request 2
Then deleting duplicates i will be left only with
Request 1
Request 2
Then I can concatenate columns i want going by Request 1 or request 2 criteria(index match), but I cant figure out how to do it...
You can use array formula to work out the start and end rows, like =SMALL(IF($A$2:$A$20<>"",ROW($A$2:$A$20)),ROW()) to find the next populated cell in A1:A20, where this would be in the cell G1. So in G1, I have a fixed 1, then in G2 down, I have =H1+1, then in each H filled down I have =SMALL(IF($A$2:$A$20<>"",ROW($A$2:$A$20)),ROW()) this gives the following
Unfortunately we cant do the concat using what we have in Excel, so this will help with your loop start and ends. Number of products, is the difference in the 2
If I am reading your question correctly, then the following code may help. You want to be able to add element rows beneath categories rows, and when you do that it changes the row number for every row beneath the new row. This code will show you that it doesn't matter which row the category is on because you can find it's row number any time, and also the number of elements beneath it.
The trick is to add a word in col A of each category that will not be found in any element A value. For example, A1 might read "Category: Apples"," and there may be ten element rows under "Category: Apples" And then under those rows another category in col A will be "Category: Bananas." The code below looks for the value "Category:" in col A and gets the row number of each category line and how many elements are under it. With a little math you can figure out where to insert a new line for a new element row or what row to concatenate. And you won't need to hard code the rows numbers of the category. Just run this simple code and it will give you those row numbers to get and concatenate all rows beneath any category.
Sub findCategoryRows()
Dim lastRowColA As Long, myArray() As Variant
Dim rowOfCategoryNameArray, nameOfCategoryNameArray, categoryCounter As Long
lastRowColA = ActiveSheet.Range("A65536").End(xlUp).Row
myArray = Range("A1:A" & lastRowColA)
categoryCounter = 1
ReDim rowOfCategoryNameArray(1 To 1)
ReDim nameOfCategoryNameArray(1 To 1)
For i = 1 To UBound(myArray)
If InStr(1, Range("A" & i).Value, "Category: ") Then
rowOfCategoryNameArray(categoryCounter) = i
nameOfCategoryNameArray(categoryCounter) = Range("A" & i).Value
categoryCounter = categoryCounter + 1
ReDim Preserve rowOfCategoryNameArray(1 To categoryCounter)
ReDim Preserve nameOfCategoryNameArray(1 To categoryCounter)
End If
Next i
For i = 1 To UBound(rowOfCategoryNameArray) - 1
If i <> UBound(rowOfCategoryNameArray) - 1 Then
Debug.Print nameOfCategoryNameArray(i) & " has " & (rowOfCategoryNameArray(i + 1) - rowOfCategoryNameArray(i)) - 1 & " element rows under it."
Else
Debug.Print nameOfCategoryNameArray(i) & " has " & (lastRowColA - rowOfCategoryNameArray(i)) & " element rows under it."
End If
Next i
End Sub
I have looked at a bunch of questions like this, but I have only found formulas, and VB examples that don't appear to check the values of cells in a column.
I was able to derive the following formula in Excel:
=IF(AND(ISNUMBER(SEARCH("Cat",R2)),OR(ISNUMBER(SEARCH("5E",R2)),ISNUMBER(SEARCH("6",R2))), ISNUMBER(SEARCH("Patch",R2)), ISNUMBER(SEARCH("Cables",R2))), "CAT 5E Ethernet Cables")
The problem is that this formula only checks for 1 out of 500 possible values. This is not productive. Plus, I have to make it one big formula when I check the entire row, because if I don't, the formula overwrites the result of the previous formula...
So, Visual Basic... I think I may have better luck scripting some kind of IF ELSE or CASE statement. I just do not understand how to do this in excel. I need to achieve the same thing as the formula above, but instead of checking for one set of conditions,
I need to check for multiple, and populate the S & T columns based on the result of each set of conditions.
I found this webpage that just mentions Excel and shows a VB IF - ELSE statement. How can I make this statement check Excel columns?
I tried the selected answer in this post with no luck:
Private Sub UpdateCategories()
Dim x As Long
For x = 1 To 5000
If InStr(1, Sheet1.Range("$B$" & x), "cat") > 0 And InStr(1, Sheet1.Range("$B$" & x), "5e") > 0 Then
Sheet1.Range("$T$" & x) = Sheet1.Range("$T$" & x) & "CAT 5E Ethernet Cables (Test)"
End If
Next
End Sub
Any help is appreciated. Thanks in advance!
Assuming you choose the route of using a data table sheet to compare to your string.
You would need to have a sheet looking like this (Maybe this is not what you want because I didn't thoroughly understand how your data looks like but the idea remains). You could have sub-category if you want, as well as category, in a third column.
column A | column B
keyword |category
CAT |ATX Cases
5e |Mini-ITX Cases
important words |MicroATX Cases
...
This would need to be filled manually. I'm not sure about the amount of data you're looking at. It can be pretty rapid if you can copy/paste stuff efficiently, depending on the form of your data.
When you have that, loop using this code. I assume the data table is in Sheet1, columns A and B and the values are in Sheet2, column A.
dim listLength as integer 'number of values to look at
dim i as integer
dim dataLength as integer 'number of keywords
dim j as integer
dim keyword as string
dim value as string
listlength = Sheet2.Cells(Rows.Count, "A").End(xlUp).Row - 1 'assuming you start on row 2
datalength = Sheet1.Cells(Rows.Count, "A").End(xlUp).Row - 1 'assuming you start on row 2
for i = 2 to listLength + 1
value = Sheet2.Range("A")(i)
for j = 2 to dataLength + 1
keyword = Sheet1.Range("A")(j)
if instr(value, keyword) <> 0 then
' what happens when the keyword is present goes here
end if
next j
next i
I'm looking for an alternative to this code, but using numbers.
I want to select 5 columns, the start column is a variable, and then it selects 5 columns from this.
Columns("A:E").Select
How do I use integers instead, to reference columns? Something like below?
For n = 1 to 5
Columns("n : n + 4") .select
do sth
next n
You can use resize like this:
For n = 1 To 5
Columns(n).Resize(, 5).Select
'~~> rest of your code
Next
In any Range Manipulation that you do, always keep at the back of your mind Resize and Offset property.
Columns("A:E").Select
Can be directly replaced by
Columns(1).Resize(, 5).EntireColumn.Select
Where 1 can be replaced by a variable
n = 5
Columns(n).Resize(, n+4).EntireColumn.Select
In my opinion you are best dealing with a block of columns rather than looping through columns n to n + 4 as it is more efficient.
In addition, using select will slow your code down. So instead of selecting your columns and then performing an action on the selection try instead to perform the action directly. Below is an example to change the colour of columns A-E to yellow.
Columns(1).Resize(, 5).EntireColumn.Interior.Color = 65535
you can use range with cells to get the effect you want (but it would be better not to use select if you don't have to)
For n = 1 to 5
range(cells(1,n).entirecolumn,cells(1,n+4).entirecolumn).Select
do sth
next n
Try using the following, where n is your variable and x is your offset (4 in this case):
LEFT(ADDRESS(1,n+x,4),1)
This will return the letter of that column (so for n=1 and x=4, it'll return A+4 = E). You can then use INDIRECT() to reference this, as so:
COLUMNS(INDIRECT(LEFT(ADDRESS(1,n,4),1)&":"&LEFT(ADDRESS(1,n+x,4),1)))
which with n=1, x=4 becomes:
COLUMNS(INDIRECT("A"&":"&"E"))
and so:
COLUMNS(A:E)
In the example code below I use variables just to show how the command could be used for other situations.
FirstCol = 1
LastCol = FirstCol + 5
Range(Columns(FirstCol), Columns(LastCol)).Select
no need for loops or such.. try this..
dim startColumnas integer
dim endColumn as integer
startColumn = 7
endColumn = 24
Range(Cells(, startColumn), Cells(, endColumn)).ColumnWidth = 3.8 ' <~~ whatever width you want to set..*
You can specify addresses as "R1C2" instead of "B2". Under File -> Options -> Formuals -> Workingg with Formulas there is a toggle R1C1 reference style. which can be set, as illustrated below.
I was looking for a similar thing.
My problem was to find the last column based on row 5 and then select 3 columns before including the last column.
Dim lColumn As Long
lColumn = ActiveSheet.Cells(5,Columns.Count).End(xlToLeft).Column
MsgBox ("The last used column is: " & lColumn)
Range(Columns(lColumn - 3), Columns(lColumn)).Select
Message box is optional as it is more of a control check. If you want to select the columns after the last column then you simply reverse the range selection
Dim lColumn As Long
lColumn = ActiveSheet.Cells(5,Columns.Count).End(xlToLeft).Column
MsgBox ("The last used column is: " & lColumn)
Range(Columns(lColumn), Columns(lColumn + 3)).Select
In this way, you can start to select data even behind column "Z" and select a lot of columns.
Sub SelectColumNums()
Dim xCol1 As Integer, xNumOfCols as integer
xCol1 = 26
xNumOfCols = 17
Range(Columns(xCol1), Columns(xCol1 + xNumOfCols)).Select
End Sub
I am writing a macro in excel and I have a 10 by 10 grid that I need to input a vlookup equation into. So for the first column of data I wrote a for loop
For i = 2 to 11
Cells (i, 30) = "=vlookup (E2" & i & ", B:C, 2, False)"
Next i
And it works the way I want it. Now I would like to write an outer for loop that will allow me to go across the columns.
I started with
For j = 30 to 39
For i = 2 to 11
Cells (i, 30) = "=vlookup (E" & i & ", B:C, 2, False)"
Next i
Next j
But I need E to change to F and I can't figure out how to do that.
I also tried using the Range (Cells(__,__),Cells(__,__)).FormulaR1C1 =...
But I couldn't get that to work either, since it is moving the table as well as fixing E2 across the columns.
If anyone can help I would greatly appreciate it. If not I will just write 10 for loops one for each column.
I think all you need to do is create the first formula and add "$" to appropriately anchor the reference rows/columns at which point you can simply copy the formula across the desired rows/columns.
here is a quick synopsis of anchoring: http://excelonthegrid.wordpress.com/2012/09/26/anchor-with-dollars/
i can give you the final answers if you post the desired formula for
a) column 1 row 1
b) column 1 row 2
c) column 2 row 1
d) column 2 row 2