Concatenate alternating fixed column headers and row values - function

I have a spreadsheet with fixed column headers and variable row data, I would like to create a simple tool (lets aim for 1 click) that will concatenate the column values and row data entered by the user into an attribute string (with '=' after each header and ';' after each value).
Before looks like this:
After looks like this:
The output is required in a separate worksheet and if possible saved as a value.
The number of columns could vary so a solution that uses a defined table would be useful.
Formula used:
=CONCATENATE(before!$A$1,"=",before!$A2,";",before!$B$1,"=",before!$B2,";",before!$C$1,"=",before!$C2,";")
Any assistance would be greatly appreciated.

The following UDF will do it:
Function unionText(ttl As Range, rng As Range) As String
Dim i As Long
If ttl.Cells.Count <> rng.Cells.Count Or _
ttl.Rows.Count <> 1 Or rng.Rows.Count <> 1 Then
unionText = CVErr(xlErrValue)
Exit Function
End If
For i = 1 To ttl.Cells.Count
unionText = unionText & ttl(i) & "=" & rng(i) & ";"
Next i
End Function
It is called in the sheet like this:
=unionText(before!$A$1:$C$1,before!A2:C2)
Then copied down
Mine is obviously on the same sheet but the formula above uses your sheet reference.

You can use this macro code temporary and assuming there's only one row of values :
Sub Macro1()
' Macro1 Macro
Range("A1").Select
Dim r As Byte
Dim c as Byte
Dim stringunion As String
r = 1
c = 1
Do While Cells(r, c) <> ""
stringunion = stringunion & Cells(r, c).Value & "=" & Cells(r + 1, c) & ";"
c = c + 1
Loop
MsgBox stringunion
End Sub

Related

Search string in between HTML tags and replace

Newbie here. I have an HTML source code and would like to look for string in between header tags <h1></h1>, <h2></h2>till <h5></h5> and then convert the text to lower case except acronyms or abbreviations (these are all capitals in 2 or more characters). And make sure that all country names in between use proper case.
As an example: It will find <h1>HR Policies and Procedures for Hiring - argentina LTD</h1>
It will convert it to:<H1>HR policies and procedures for hiring - Argentina LTD</H1>
I've tried a user defined function for Excel VBA found online: CapIt(A2). It uses Search, Split and Join. I'm not able to put them together to come up with the result. Would appreciate very much your help. Thank you.
Code I saw online as initial reference:
Function Capit(s As String)
Dim v As Variant, j As Long
v = Split(s, " ") ' separates the words
For j = LBound(v) To UBound(v)
If StrComp(v(j), UCase(v(j)), vbBinaryCompare) <> 0 Then v(j) = StrConv(v(j), vbProperCase)
Next j
Capit = Join(v, " ") ' joins the words
End Function
'Added this code below, can we use the results to lowercase the string and exclude the output in this function
Function FindAcronyms(yourWord As String)
Dim I As Integer
Dim ctr As Integer
FindAcronyms = Null
For I = 1 To Len(yourWord)
If Asc(Mid(yourWord, I, 1)) <= 90 And _
Asc(Mid(yourWord, I, 1)) >= 65 Then
If ctr > 0 Then
FindAcronyms = FindAcronyms & Mid(yourWord, I - 1, 1)
End If
ctr = ctr + 1
Else
If ctr > 1 Then
FindAcronyms = FindAcronyms & Mid(yourWord, I - 1, 1) & ", "
End If
ctr = 0
End If
Next
If ctr > 1 Then
FindAcronyms = FindAcronyms & Mid(yourWord, I - 1, 1)
End If
If Right(FindAcronyms, 2) = ", " Then
FindAcronyms = Left(FindAcronyms, Len(FindAcronyms) - 2)
End If
End Function
'the final look would be something like this
Sub TitleChange()
'define array
myarray = Range("A1:A100")
' Define the pattern
Dim pattern As String: pattern = "<h*>*</h*>" 'looks for the header tags
Dim f As Variant
For Each f In myarray
If f Like pattern = True Then Capital (f) 'changes all string to lower case except countries (to retain proper case) and acronyms (to retain uppercase)
Next f
End Sub
You can include the countries in an array
Sub Test()
Debug.Print Capital("HR Policies and Procedures for Hiring - argentina LTD")
End Sub
Function Capital(ByVal s As String)
Dim a, v As Variant, j As Long
a = Array("Argentina", "Egypt", "Enland")
v = Split(s, " ")
For j = LBound(v) To UBound(v)
If StrComp(v(j), UCase(v(j)), vbBinaryCompare) <> 0 Then v(j) = StrConv(v(j), vbLowerCase)
If Not IsError(Application.Match(v(j), a, 0)) Then v(j) = StrConv(v(j), vbProperCase)
Next j
Capital = Join(v, " ")
End Function
Added UDF that parses HTML code, used the Sub Test above as UDF Capital and UDF to bring together. Welcome suggestions to make it cleaner or more efficient
Dim rng As Range, cell As Range
Set rng = Range("A1:A5")
' Define the pattern
Dim pattern As String: pattern = "*<h?>*</h?>*"
' Check each item against the pattern
For Each cell In rng
If (cell Like pattern = True) Then
cell.Offset(0, 16).Value = cell.Value
cell.Offset(0, 16).Value = joinCell(Capital(StripHTML(cell)), cell.Offset(0, 0).Value) 'used UDF for striping innertext, applying rules and joining back string
End If
Next cell
End Sub

Matching values in one column with comma separated values in another column and returning the matched value

I have one column with values like
himaanshu
akshay
rahul
hgeet
And another column with values like
axs,fdvf,dasad
axs,fdvf,dasad, himaanshu
axs,fdvf,dasad, akshay
asz,wesd,hgeet
I need to return the matching name for every row in Column 2 from whole list of Column 1
Solution Should be:
1. None
2. himaanshu
3. akshay
4. hgeet
Can anyone help me with the formula that I can use in spreadsheet to solve this.
Try the below:
Sub test()
Dim str1 As String
Dim rngToSearch As Range, cell As Range
Dim LastRowA As Long, LastrowC As Long, i As Long, y As Long
Dim arr As Variant
With ThisWorkbook.Worksheets("Sheet1")
LastRowA = .Cells(.Rows.Count, "A").End(xlUp).Row
LastrowC = .Cells(.Rows.Count, "C").End(xlUp).Row
Set rngToSearch = .Range("C2:C" & LastrowC)
For i = 2 To LastRowA
str1 = .Range("A" & i).Value
For Each cell In rngToSearch
arr = Split(cell.Value, ",")
For y = LBound(arr) To UBound(arr)
If Trim(arr(y)) = Trim(str1) Then
.Range("B" & i).Value = str1
End If
Next y
Next cell
Next i
End With
End Sub
Results:
See if this formula works (in a google spreadsheet)
=ArrayFormula(iferror(REGEXEXTRACT(C2:C5, textjoin("|", 1, A2:A5)), "none"))
The formula extracts any of the values in column A from the values in column C
[
=VLOOKUP("*"&A1&"*", B1:B4,1,0)

Select randoms records from table but not more than 2 records of same name

I want to fetch TOP N random records from the table but not more than 2 records for same name.
SELECT TOP 7 Table1.ID, Table1.Name, Table1.Salary, Rnd(Abs([Table1]![id])) AS Expr1
FROM Table1
GROUP BY Table1.ID, Table1.Name, Table1.Salary, Rnd(Abs([Table1]![id]))
ORDER BY Rnd(Abs([Table1]![id]));
It is giving more than two records for same name. Would someone please provide some assistance.
Use this query:
SELECT
ID,
[Name]
FROM
[Table1]
ORDER BY
Rnd(-Timer()*[ID]);
Then open it as a Recordset and traverse it from the start and pick IDs (could be saved in an array) while recording the the Name used (a Collection could be used for this).
If a Name has been used twice, skip the record and move to the next.
When you have picked seven IDs, stop. The array of IDs will identify your seven records.
Save the query as RandomAll. Then use it in this function:
Public Function RandomTwo() As long()
Dim rs As DAO.Recordset
Dim Names As New Collection
Dim Used As Integer
Dim Index As Integer
Dim Ids() As Long
Set rs = CurrentDb.OpenRecordset("RandomAll")
ReDim Ids(0)
Do While Not rs.EOF
Used = 0
' Read used count. Will fail if not used.
On Error Resume Next
Used = Val(Names.Item(rs.Fields(1).Value))
On Error GoTo 0
Debug.Print Used, ;
If Used = 1 Then
' Remove key to be added later with updated use count.
Names.Remove rs.Fields(1).Value
End If
If Used < 2 Then
' Record the use count (as text) of the key.
Names.Add CStr(Used + 1), rs.Fields(1).Value
Debug.Print rs!ID.Value, rs.Fields(1).Value
' Add ID to array.
Ids(UBound(Ids)) = rs!ID.Value
If UBound(Ids) = 6 Then
' Seven IDs found.
Exit Do
Else
' Prepare for next ID.
ReDim Preserve Ids(UBound(Ids) + 1)
End If
End If
rs.MoveNext
Loop
rs.Close
' List the found IDs.
For Index = LBound(Ids) To UBound(Ids)
Debug.Print Index, Ids(Index)
Next
' Return the IDs.
RandomTwo = Ids
End Function
The function will return the array holding the seven IDs.
Taking inspiration from Gustav's answer I have designed a bit of VBA code that will generate a SQL string which when used will give you N amount of random records with a limit of 2 per name.
Const PicksLimit As Long = 7 'How many records do you want to select
Dim rs As DAO.Recordset
'Select randomised table
Set rs = CurrentDb.OpenRecordset("SELECT ID, Name From Table1 ORDER BY Rnd(Abs(ID))")
'Define variables for keeping track of picked IDs
Dim Picks As Long, PickNames As String, PicksSQL As String
Picks = 0
PickNames = ""
PicksSQL = ""
With rs
If Not (.BOF And .EOF) Then 'If table is not empty...
.MoveFirst
'Loop until limit reached or table fully looked through
Do Until Picks = PicksLimit Or .EOF
'If name has been picked less than twice before
If Len(PickNames) - Len(Replace(PickNames, "[" & !Name & "]", "")) < ((Len(!Name) + 2) * 2) Then
Picks = Picks + 1 'Increment counter
PickNames = PickNames & "[" & !Name & "]" 'Add name for later checks
PicksSQL = PicksSQL & "ID = " & !Id & " OR " 'Append SQL string
End If
.MoveNext
Loop
'Add front sql section and remove last OR
PicksSQL = "SELECT * FROM Table1 WHERE " & Left(PicksSQL, Len(PicksSQL) - 4)
Else
'If the table is empty no need for ID checks
PicksSQL = "SELECT * FROM Table1"
End If
End With
rs.Close
Set rs = Nothing
'Print SQL String (This can be changed to set a RecordSource or similar
Debug.Print (PicksSQL)
At the moment the SQL string is just printed to the Immediate window but this can be changed to go wherever you need, like a subform's RecordSource for instance.
The code will need to be run every time you want a new random list but it shouldn't take a huge amount of time so I don't see that being too big an issue.

Variable not working inside IN clause

This code is written in Excel2010 VBA and queries PostGreSQL tables
I have the following code in VBA that creates a variable that I would like to use in my SQL query, but I cannot get the SQL query to accept the VBA variable using the IN clause
This code creates the variable I want to use and works fine. It allows me to select specific cells I need to query
Dim StaffID As Range
Dim ID As Range
Dim LR As Long
Dim SelectedID As String
'Count number of rows to search
LR = Worksheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row
On Error Resume Next 'if only 1 row
'Store Data from here
Set StaffID = ThisWorkbook.Sheets("Sheet1").Range("B2:B" & LR)
'Loop through each cell in Range and look for any character in column A
'then store offset cell value using comma delimiter
For Each ID In Worksheets("Sheet1").Range("A2:A" & LR).Cells
If ID.Value > 0 Then
SelectedID = SelectedID & "," & ID.Offset(, 1).Value
End If
Next ID
'Remove first delimiter from string (,)
SelectedID = Right(SelectedID, Len(SelectedID) - 1)
OUTPUT EXAMPLE of SelectedID = 6,7,8,6452
I now want to add this to my query using the IN clause, but it just doesn't work. Does anyone have a solution or workaround.
Sheets("Sheet2").Select
Range("A1").Select
Dim rs As Recordset
Dim t As String
t = "SELECT DISTINCT s.entity_id, u.login_name, s.role " _
& "FROM staff s INNER JOIN user u ON s.entity_id=u.staff_id " _
& "WHERE u.staff_id IN (SelectedID) " _
Set rs = conn.Execute(t)
With ActiveSheet.QueryTables.Add(Connection:=rs, Destination:=Range("A1"))
.Refresh
End With
rs.Close
At the moment you're putting the string 'SelectedID' directly into your query. You'll need something like this in your VBA:
t = "SELECT DISTINCT s.entity_id, u.login_name, s.role " _
& "FROM staff s INNER JOIN user u ON s.entity_id=u.staff_id " _
& "WHERE u.staff_id IN (" & SelectedID & ")"
Rows.Count should be fully qualified and With ThisWorkbook.Worksheets("Sheet1") and fully qualifying will make your code read better.
Function getSelectedIDSQL() As String
Dim ID As Range, StaffID As Range
Dim SelectedID As String
With ThisWorkbook.Worksheets("Sheet1")
For Each ID In .Range("A2" & .Range("A" & .Rows.Count).End(xlUp))
If ID.Value > 0 Then SelectedID = SelectedID & "," & ID.Offset(, 1).Value
Next
End With
If Len(SelectedID) Then getSelectedIDSQL = Right(SelectedID, Len(SelectedID) - 1)
End Function

Populating access table from form multi-record textbox

I am trying to use this code to pick comma seperated numbers from ExcUID text box of form and then feed them into tblExcIndivList table.
However what I am trying to do it to split ex: 123,1213 into lines and put them in seperate rows of UID column of tblExcIndivList table but it gets saved as 1231213 in the same cell.
Sub Upd_UID()
Dim var As Variant
Dim i As Long
var = Split(Forms.Agen_Report.ExcUID.Value, vbNewLine)
CurrentDb.Execute "DELETE * FROM tblExcIndivList;", dbFailOnError
For i = 0 To UBound(var)
CurrentDb.Execute Replace("INSERT INTO tblExcIndivList ( UID ) VALUES ( '#V' );", "#V", var(i)), dbFailOnError
Next i
End Sub
Please help.
You are not splitting correctly your string, you say it is comma-separated (i.e. 123,1213) and try to split it with vbNewLine. You should specify the comma as separator:
var = Split(Forms.Agen_Report.ExcUID.Value, ",")
This will get you past this error and split correctly the input. However I cant make sure whether your query is well-formed.
I think you need something like this.
Option Explicit
Dim aCell As Range
Private Sub UserForm_Initialize()
'~~> Change Sheet1 to the relevant sheet name
'~~> Change A1:E1 to the relevant range
For Each aCell In ThisWorkbook.Sheets("Sheet1").Range("A1:E1")
If InStr(1, aCell.Value, ",") Then _
ComboBox1.AddItem Split(aCell.Value, ",")(0)
Next aCell
'~~> Remove duplicates
RemoveDuplicates ComboBox1
End Sub
Private Sub ComboBox1_Click()
Dim tmpStr As String
ComboBox2.Clear
For Each aCell In ThisWorkbook.Sheets("Sheet1").Range("A1:E1")
If InStr(1, aCell.Value, ",") Then _
tmpStr = Split(aCell.Value, ",")(0)
If Trim(ComboBox1.Value) = Trim(tmpStr) Then _
ComboBox2.AddItem aCell.Value
Next aCell
End Sub
'~~> Procedure to remove duplicates
Private Sub RemoveDuplicates(cmb As ComboBox)
Dim a As Integer, b As Integer, c As Integer
a = cmb.ListCount - 1
Do While a >= 0
For b = a - 1 To 0 Step -1
If cmb.List(b) = cmb.List(a) Then
cmb.RemoveItem b
a = a - 1
End If
Next b
a = a - 1
Loop
End Sub