Linking two different Excel tables and auto-populate one when populate another - excel

I have two diferent excel formatted tables one near another. First table (green headers) is a table where I have to add some chemical formulas in Column A and Column B will be SUM of all compounds that I will add from second table (yellow headers), which represents Periodic System of Elements!
The formula that I am using in Table 2 (yellow headers) for calculating chemical compounds is this:
=C$2*MAX(IFERROR(IF(FIND(C$1&ROW($1:$99);MolM.[#[Mol. Formula]]);ROW($1:$99);0);0);IFERROR(IF(FIND(C$1&CHAR(ROW($65:$90));MolM.[#[Mol. Formula]]&"Z");1;0);0)) (CSE formula)
What and how I am usually doing this update of new compounds is that I am adding new chemical formulas in Column A manually (that is okay) and then dragging main formula in Table 2 (yellow header) to calculate all elements, and then SUM in column B for the main result!
My question is, is there a possibility to be more automated, just when I type new compound in Column A it will expand as normal table do, but also to auto-expand and calculate rest of compounds, without that I drag the formula manually..?
Hopefully this was clear enough.
Is there any possibility to make this happen? Is the only solution Power Query or?

I'm making a wild guess here.
Say that you write NH3 in A3, and then have it print I2 (value of "N") in I3, and C2 * 3 (value of "H" times 3, "H3") in C3. To then have B3 calculate the total value with =SUM() or similar.
You could have a VBA sub that looks for the value and prints this.
Here is a prototype of that:
Sub molFunc(chem As String, formRow As Long)
Dim i As Long, c As String, atoms As Range, a as Range
Set atoms = Range("C1", Cells(1, Columns.count).End(xlToLeft))
For i = 1 To Len(chem)
If Not Mid(chem, i + 1, 1) = UCase(Mid(chem, i + 1, 1)) Then
c = Mid(chem, i, 2)
i = i + 1
Else
c = Mid(chem, i, 1)
End If
For Each a In atoms
If a.Value = c Then
If IsNumeric(Mid(chem, i + 1, 1)) Then
a.Offset(formRow - 1).Value = a.Offset(1) * Mid(chem, i + 1, 1)
Else
a.Offset(formRow - 1).Value = a.Offset(1)
End If
End If
Next a
Next i
End Sub
Then you can call it from a Worksheet_Change event in the worksheet of your choice.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A:A")) Is Nothing And Target.count = 1 Then
Application.EnableEvents = False
Call molFunc(Target.Value, Target.Row)
Application.EnableEvents = True
End If
End Sub

Related

Selecting number in column "x" randomly to achieve desired number

I have numbers in a column (i.e., 1 to 10 in column A) and a few numbers in another column (i.e six numbers in column E).
I want to place numbers of column E in column B randomly, so that absolute difference between An and Bn is more than my desirable number (D1).
I used RandomSelection function:
Function RandomSelection(aRng As Range)
Dim index As Integer
Randomize
index = Int(aRng.Count * Rnd + 1)
RandomSelection = aRng.Cells(index).Value
End Function
Put this in B2 and copy down:
=AGGREGATE(15,6,$E$2:$E$7/(ABS($E$2:$E$7-A2)>=$D$1),RANDBETWEEN(1,SUMPRODUCT(--(ABS($E$2:$E$7-A2)>=$D$1))))
I changed the numbers in E2:E7 so it would return values that are greater. As stated in the comments. Getting numbers 1-6 to work with 1-10 left many errors as one cannot find a number 1-6 that returns a number greater than 5 with an X of 2-5.
You can accomplish what you want by using RANDBETWEEN and ABS Formulas.
Dim RndmzRng As Range
Set RndmzRng = Range("B2:B21")
Dim AbsValRng As Range
Set AbsValRng = Range("C2:C21")
Dim cel As range
With ActiveSheet
RndmzRng.Formula = "=RANDBETWEEN(1,6)" 'so you don't need data in ColE
'can be use with cell references, "=RANDBETWEEN($E$2,$E$3") where E2=1 and E3=6
AbsValRng.Formula = "=ABS(B2-A2)" 'Absolute formula
For Each cel In AbsValRng 'colors the cells green that are > the value in Range("D1")
If cel.Value > Range("D1").Value Then
cel.Value = cel.Value - Range("D1").Value
End If
Next
End With

Excel VBA: Dynamically Changing Function Based on Column

Heylo, I am trying to write an excel function that takes a user-selected range and performs different calculations based on the column the cell being populated lines up with. The screenshot below shows the setup of the columns.
I want to set AA5 to be "=myFunction($AA1:$AD4)", and then I want click-and-drag to use the autofill feature to populate AB5, AC5, and AD5 with the same "=myFunction($AA1:$AD4)" but this myFunction will do different things based on which cell is being populated during the autofill.
I know how to do this in a subroutine where the user would select the first open cell AA5, and is prompted for the range to use for calculations. I would do something along the lines of:
Sub CalcCells()
Dim myRange As Range
Set myRange = Application.InputBox("Select the cells you want to use...", Type:=8)
Dim numColumn As Long
For numColumn = 0 To myRange.Columns.Count - 1
Select Case numColumn
Case Is = 0
ActiveCell.Offset(0, numColumn).Formula = "=SUM(" + myRange.Columns(1) + ")"
Case Is = 1
ActiveCell.Offset(0, numColumn).Formula = "=SUMPRODUCT(" + myRange.Columns(1) + "," + myRange.Columns(2) + ")"
Case Is = 2
ActiveCell.Offset(0, numColumn).Formula = "=SUMPRODUCT(" + myRange.Columns(1) + "," + myRange.Columns(3) + ")/SUM(" + myRange.Columns(1) + ")"
Case Is = 3
ActievCell.Offset(0, numColumn).Formula = "=SUMSQ(" + myRange.Columns(4) + ")"
End Select
Next numColumn
End Sub
So basically I want to do exactly this, but I want it to be a function that when I click and drag and autofill AB5:AD5 it knows which column the cell lines up with and performs a calculation based on that, and uses it as an argument/parameter almost. It will not always be 4 rows either, so it needs to be capable of accommodating varying row counts, but the .Columns should work with that as long as the user selects only the same datatype.
Is this possible and how can I do it? Thank you for any help in advance. I've done a lot of searching and I don't know if I'm not searching the right way, but i cannot find anything that really helps.
What about something like this? Basically, you get the column of the cell you enter the formula into with Application.Caller.Column. Then inputRange.Column gives you the leftmost column of your input range. Based on the difference of the two, you know which worksheet function you want to use. If the difference is 0, your formula is entered in the 1st column, so you use Sum. If the difference is 1, you use Sumproduct, and so on.
Function SummarizeCells(inputRange As Range) As Double
Dim col As Long
col = Application.Caller.Column - inputRange.Column
Select Case col
Case 0
SummarizeCells = WorksheetFunction.Sum(inputRange.Columns(1))
Case 1
SummarizeCells = WorksheetFunction.SumProduct(inputRange.Columns(1), inputRange.Columns(2))
Case 2
SummarizeCells = WorksheetFunction.SumProduct(inputRange.Columns(1), inputRange.Columns(3)) / WorksheetFunction.Sum(inputRange.Columns(1))
Case 3
SummarizeCells = WorksheetFunction.SumSq(inputRange.Columns(4))
End Select
End Function
A sample view here:

Recalculating Excel VBA Function on one sheet disrupts other sheets

I made a function that counts the number of items in a given month.
Column A is the month, and Column B is the number of items in that month.
Cell B1 has:
=countItems(A1)
Excel data:
Code:
Function countItems(month)
Application.Volatile
Dim count As Integer
If Not (month = 0) Then
count = 0
Do While Not (Cells(month.row + count + 1, 3) = 0)
count = count + 1
Loop
countItems = count
Else
countItems = ""
End If
End Function
I dragged the formula down from B1 to B500 and it works properly for every month. The formula returns nothing if there is no month in the corresponding A cell.
I have multiple sheets using the same formula on similarly-structured data sets.
Whenever the values in column B update for this Sheet 1, the other sheets will change too. However, Sheet 2 will update using Column C from Sheet 1.
If I have Sheet 2 recalculate, Sheet 1 will update using Column C from Sheet 2.
The function counts the number of items in a given month by checking how far down it can read in Column C before it finds the blank cell, indicating that the month is over.
Sheet 2 has 1 item in the first month, but it will still return 3 due to Sheet 1 having 3 items (counts Row 1 through 3 and stops at Row 4).
The second month of Sheet 2 begins on Row 3. But since the function is reading Column C from Sheet 1, it will run into the blank cell after counting 1 more item (counts Row 3 and stops at Row 4). Therefore no matter how many items are in Sheet 2 Month 2, it will return 1.
The function always uses the correct Column A, and only displays a number in Column B where there is date in Column A.
The consequence is that only 1 sheet can have the correct values, and doing that disrupts the other sheets.
I cannot solve this at the moment because I am new to VBA.
I have thought of making all of the function's cell references include a self-reference to the current cell's sheet, but I don't know how to do that and I don't know if it would work.
Edit: I couldn't make it work this way, but Application.Caller.Offset() with relative cell position worked as a solution. I am still wondering if there is a way to use absolute cell position though.
The sheets are not grouped together.
it's because there's a "time-space shift" between the range passed to the function and the range "felt" as the "caller" one by the function
you can see this behavior by modifying the function code as follows
Function countItems(month)
Application.Volatile
Dim count As Integer
Dim r As Range
Dim p As Variant, a As Variant
Set r = Application.Caller '<~~ retrieve the actual "calling cell" of the current function "instance"
p = r.Parent.Name
a = r.Address
MsgBox p & " vs " & month.Parent.Name & vbCrLf & a & " vs " & month.Address '<~~ compare the function "calling cell" vs the "passed cell"
If Not (month = 0) Then
count = 0
Do While Not (Cells(month.Row + count + 1, 3) = 0)
count = count + 1
Loop
countItems = count
Else
countItems = ""
End If
End Function
and you'll see msgboxs prompts showing you differences between the function "calling cell" and "passed cell" addresses and/or sheets
so to avoid this behavior you could rely on the "calling range only", like follows:
Option Explicit
Function countItems(month)
Application.Volatile
Dim r As Range
Set r = Application.Caller '<~~ retrieve the actual "calling cell" of the current function "instance"
'the "calling cell" is the one with the called function in its formula, i.e. in column "B" as per your data structure. then ...
If Not IsEmpty(r.Offset(, -1)) Then '<~~ ... the column with dates are one column to the left, and ...
Do While Not IsEmpty(r.Offset(countItems + 1, 1)) '<~~ ... the values to be checked for are one column to the right
countItems = countItems + 1
Loop
End If
End Function

How do I compare a ratio in VBA?

I have two integer values in 2 columns in excel. I need to retrieve these integers and form a ratio and then compare it with another ratio in another column.
For example, if the numbers are 2 and 3 in columns A1 & B1. I need to retrieve these values and form 2:3 in C1 and should compare if this is same as in D1 (with value 4:1).
How do i do it in VBA ?
I created a formula in C1 as =A1&":"&B1 and got the values in C1. However i am unable to compare this with the value in D1, and I am not sure how do I declare this variable in VBA ?
Kindly help me here.
You can try this ..
Private Sub Worksheet_Change(ByVal Target As Range)
Dim y As Integer
y = Target.Row
Select Case Target.Column
Case 1, 2 ' if col A
If IsEmpty(Cells(y, 1)) Or IsEmpty(Cells(y, 2)) Then
Cells(y, 3) = ""
Exit Sub
End If
Cells(y, 3) = "'" & Cells(y, 1) & ":" & Cells(y, 2)
Case 3 ' then compare
MsgBox IIf(Cells(y, 3) = Cells(y, 4), "Equal", "Not Equal")
End Select
End Sub
This use ' sign to avoid autoconvert value to time and for this you should have in D column with ' too ..
If D1 is formatted in the same way as C1 then a formula of =(D1=C1) would return TRUE or FALSE. In VBA this could be stored in a variable:
Dim blnSame As Boolean
blnSame = (Range("D1").Value = Range("C1").Value)
Based on the OPs further comment, an Excel formula would be:
=IF(Sheet1!C1=Sheet2!D1,"Win","Lose") ' or ,"Lose","Win"
(adjust the cell and sheet references as necessary) which could be copied down the column.
In VBA, a formula could be inserted into a range:
Worksheets("Sheet3").Range("E1:E10").Formula = _
"=IF(Sheet1!C1=Sheet2!D1,""Win"",""Lose"")"
If you want to loop down the column inserting the words "Win" or "Lose" then IMO you should study some VBA.

How do I get excel to merge cells only when other cells are filled?

I need to merge cells using a formula so that the cells only merge when cells on another tab are filled.
I have 2 tabs with the same amount of columns in each. I want cells a1-d1 to merge in tab 1 when cells a1-d1 in tab 2 are filled and for the value of d1 in tab 2 to be inputted into the newly merged cells in tab 1.
this is what I have:
Excel VBA Methods and Function (Excel Macros) overview
Since you want to change cells i do not believe that you can use a formula (even not a user defined one). Therefore i wrote an excel vba macro for your problem.
FirstRows(): Is the starting point. It loops over 10 rows and calls the other methods
CheckEmptyCellValues(curRow): This method checks for empty cells in tab2 (sheet 2 in excel)
MergeCells(curRow) takes the current row as a number (any integer from 1 to max amount of rows) and merges the cells from column 1 to 4 on Sheet 1 (the first sheet in excel)
Fully working demo tested with 4 columns and 10 rows
Sub FirstRows()
Sheets(1).Select
For curRow = 2 To 11
Merge = CheckEmptyCellValues(curRow)
If Merge = 4 Then
MergeCells (curRow)
cellValue = Sheets(2).Cells(curRow, 4).Value
Sheets(1).Cells(curRow, 1).Value = cellValue
End If
Next
End Sub
Sub MergeCells(curRow)
Sheets(1).Select
Range(Cells(curRow, 1), Cells(curRow, 4)).MergeCells = True
End Sub
Function CheckEmptyCellValues(curRow)
Merge = 0
Sheets(2).Select
For i = 1 To 4
If Len(Cells(curRow, i).Value) > 0 Then
Merge = Merge + 1
End If
Next
CheckEmptyCellValues = Merge
End Function
Below you can see the result. The values from sheet 2 haven been copied to sheet 1 (second image). In Sheet 1 the Cells in a row are merged (in row 2 from Cell A2 up to Cell D2 (A2-D2 is now just one cell) if in the first image (sheet 2) every cell (from column a to column d) in a row had a value.
Bugs in the modified code
There are a few things in the modifiend code that are not possible or could lead to a wrong understanding
Function CheckEmptyCellValues(curColumn)
Merge = 0
Sheets(2).Select
For i = A To d
If Len(Cells(curColumn, 11).Value) > 0 Then
Merge = Merge + 1
End If
Next
CheckEmptyCellValues = Merge
End Function
The line For i = A To d is not possible. If you want to use a loop you have to use numbers: For i = 1 To 4 this would repeat the code between For and Next4 times starting with 1
This line Cells(curColumn, 11).Value is technical correct but misleading. Excel uses the first value after (for the row-index and the second value for the column-index. Both values have to be a number: Cells(4,2).Value returns the Cell value from the 4th. row and the second Column (in the Excel Gui the Cell B4)
Try changing this line For i = A To d to this For i = 1 To 4 and see if that returns the wished result.
Bugs part 2
In your other modification you have some of the same bugs:
The loop For curColumn = A to d needs numbers instead of letters (unless A and d were a variable filled with a number but according to your code sample this is not the case
The line cellValue = Sheets(2).Cells(curColumn, d).Value has the same bug, if d is just the letter d and not something like d = 4 than you can not use it in a loop.
This is the code from your comment:
Sub FirstRows()
Sheets(1).Select
For curColumn = A To d
Merge = CheckEmptyCellValues(curColumn)
If Merge = d Then
MergeCells(curColumn)
cellValue = Sheets(2).Cells(curColumn, d).Value
Sheets(1).Cells(curColumn, d).Value = cellValue
End If
Next
End
Sub Sub MergeCells(curColumn)
Sheets(1).Select
Range(Cells(curColumn, 1), Cells(curColumn, d)).MergeCells = True
End Sub
Be carefull it is not running.

Resources