How to show sub categories under main category - vbscript

I need some help on classic ASP.
Currently, the output shows like:
cat
south
cat
south
cat2
east
cat2
west
I don't want to see the main categories repeating like that.
How do I combine main category into one and show sub categories below main?
Like:
cat
south
south
cat2
east
west
Category table
ID Category
------------
01 cat
02 cat2
03 cat3
Subcat table
ID Subcat Category_id
-------------------------
1 south 01
2 north 01
3 east 02
4 west 02
5 line 03
Code:
<%
sSQL = " SELECT s.*, c.* FROM Category c, Subcat s WHERE s.Subcat_id = c.Subcat_id "
objRS.Open sSQL, objCon
response.Write sSQL
Do Until objRS.EOF
sCategory = objRS("Category")
sSubcat = objRS("Subcat")
%>
<input type="text" name="CategoryN" size="40" maxlength="50" value="<%= sCategory %>"><br />
<input type="text" name="SubcatN" size="40" maxlength="50" value="<%= sSubcat %>"><br />
<%
objRS.MoveNext
Loop
objRS.Close
%>
Thanks very much!

Something like this:
<%
currCat = "~~~~~~"
sSQL = "SELECT s.*, c.* FROM Category c, Subcat s WHERE s.Subcat_id = c.Subcat_id "
objRS.Open sSQL, objCon
response.Write sSQL
Do Until objRS.EOF
sCategory = objRS("Category")
sSubcat = objRS("Subcat")
if currCat <> sCategory Then
currCat = sCategory
<%
<input type="text" name="CategoryN" size="40"
maxlength="50" value="<%=sCategory%>"><br />
%>
End If
%>
<input type="text" name="SubcatN" size="40"
maxlength="50" value="<%= sSubcat %>"><br />
<%
objRS.MoveNext
Loop
objRS.Close
%>

Related

How can I add paging for results in a table created in Classic ASP?

I have some code done in VBScript that creates a table. Specifically, the code pulls information from a database and then loops through the result adding them to a table. The problem is that there are 14,000 rows in this table. Every time this page tries to load, I get a 500 Internal Server error which I assume is due to lack of memory.
For the loop, I have this:
<%
fHideNavBar = False
fHideNumber = False
fHideRequery = False
fHideRule = False
stQueryString = ""
fEmptyRecordset = False
fFirstPass = True
fNeedRecordset = False
fNoRecordset = False
tBarAlignment = "Left"
tHeaderName = "DataRangeHdr1"
tPageSize = 0
tPagingMove = ""
tRangeType = "Text"
tRecordsProcessed = 0
tPrevAbsolutePage = 0
intCurPos = 0
intNewPos = 0
fSupportsBookmarks = True
fMoveAbsolute = False
If IsEmpty(Session("DataRangeHdr1_Recordset")) Then
fNeedRecordset = True
Else
If Session("DataRangeHdr1_Recordset") Is Nothing Then
fNeedRecordset = True
Else
Set DataRangeHdr1 = Session("DataRangeHdr1_Recordset")
End If
End If
If fNeedRecordset Then
Set DataConn = Server.CreateObject("ADODB.Connection")
DataConn.Open "DSN=MYDSN","MyUserName","MyPassword"
Set cmdTemp = Server.CreateObject("ADODB.Command")
Set DataRangeHdr1 = Server.CreateObject("ADODB.Recordset")
cmdTemp.CommandText = "SELECT PHONE, FAX, FIRM, ID FROM NNYBEA ORDER BY ID"
cmdTemp.CommandType = 1
Set cmdTemp.ActiveConnection = DataConn
DataRangeHdr1.Open cmdTemp, , 0, 1
End If
On Error Resume Next
If DataRangeHdr1.BOF And DataRangeHdr1.EOF Then fEmptyRecordset = True
On Error Goto 0
If Err Then fEmptyRecordset = True
If Not IsEmpty(Session("DataRangeHdr1_Filter")) And Not fEmptyRecordset Then
DataRangeHdr1.Filter = Session("DataRangeHdr1_Filter")
If DataRangeHdr1.BOF And DataRangeHdr1.EOF Then fEmptyRecordset = True
End If
If fEmptyRecordset Then
fHideNavBar = True
fHideRule = True
End If
Do
If fEmptyRecordset Then Exit Do
If Not fFirstPass Then
DataRangeHdr1.MoveNext
Else
fFirstPass = False
End If
If DataRangeHdr1.EOF Then Exit Do
%>
<tr>
<td><p align="center"><%= DataRangeHdr1("FIRM") %></td>
<td><p align="center"><%= DataRangeHdr1("PHONE") %></td>
<td><p align="center"><%= DataRangeHdr1("FAX") %></td>
<%end if%>
</tr>
<%
Loop%>
Now, I believe that the programmer before me essentially copied the code from this website: http://www.nnybe.com/board%20members/DEFAULT.ASP
In fact, I actually changed the column names in my loop to match the website, since it was so similar (my real column names are different). After the loop, the code I have is as follows:
</TABLE>
<%
If tRangeType = "Table" Then Response.Write "</TABLE>"
If tPageSize > 0 Then
If Not fHideRule Then Response.Write "<HR>"
If Not fHideNavBar Then
%>
<TABLE WIDTH=100% >
<TR>
<TD WIDTH=100% >
<P ALIGN=<%= tBarAlignment %> >
<FORM <%= "ACTION=""" & Request.ServerVariables("PATH_INFO") & stQueryString & """" %> METHOD="POST">
<INPUT TYPE="Submit" NAME="<%= tHeaderName & "_PagingMove" %>" VALUE=" << ">
<INPUT TYPE="Submit" NAME="<%= tHeaderName & "_PagingMove" %>" VALUE=" < ">
<INPUT TYPE="Submit" NAME="<%= tHeaderName & "_PagingMove" %>" VALUE=" > ">
<% If fSupportsBookmarks Then %>
<INPUT TYPE="Submit" NAME="<%= tHeaderName & "_PagingMove" %>" VALUE=" >> ">
<% End If %>
<% If Not fHideRequery Then %>
<INPUT TYPE="Submit" NAME="<% =tHeaderName & "_PagingMove" %>" VALUE=" Requery ">
<% End If %>
</FORM>
</P>
</TD>
<TD VALIGN=MIDDLE ALIGN=RIGHT>
<FONT SIZE=2>
<%
If Not fHideNumber Then
If tPageSize > 1 Then
Response.Write "<NOBR>Page: " & Session(tHeaderName & "_AbsolutePage") & "</NOBR>"
Else
Response.Write "<NOBR>Record: " & Session(tHeaderName & "_AbsolutePage") & "</NOBR>"
End If
End If
%>
</FONT>
</TD>
</TR>
</TABLE>
<%
End If
End If
%>
</TABLE>
I'm guessing from the < and > around the PagingMove part, this is supposed to allow paging. However, I'm not even seeing this on my page. I don't know if the code on the link above works on their website, but for my own website I'd ask:
How can I modify this code to provide an option to click through pages of the data result so the server doesn't run out of memory?
If there is a more elegant solution to this that can accomplish the same thing, I'd appreciate that as well!!!
In your SQL you could add a LIMIT offset
SELECT PHONE, FAX, FIRM, ID FROM NNYBEA ORDER BY ID LIMIT 0,10 ' Results 1 to 10
SELECT PHONE, FAX, FIRM, ID FROM NNYBEA ORDER BY ID LIMIT 10,10 ' 11 - 20
SELECT PHONE, FAX, FIRM, ID FROM NNYBEA ORDER BY ID LIMIT 20,10 ' 21 - 30
...
If you're using MySQL you can use...
SELECT SQL_CALC_FOUND_ROWS PHONE, FAX, FIRM, ID FROM NNYBEA ORDER BY ID LIMIT 0,10
... to get a total count of the results and calculate the number of page links to display:
(total_results/results_per_page) ' and round up.
Then link to the pages below the results table and pass the page numbers as a query string:
default.asp?page=1
default.asp?page=2
default.asp?page=3
...
Have some code at the top of your page that gets the requested page number and calculates the correct offset value:
<%
Const results_per_page = 10
Dim limit_offset, page_num
limit_offset = 0 ' default
page_num = request.querystring("page")
if isNumeric(page_num) then
page_num = int(page_num)
if page_num > 0 then
limit_offset = (page_num-1)*results_per_page
else
page_num = 1 ' default
end if
else
page_num = 1 ' default
end if
%>
Finally, apply the limit offset to your SQL:
cmdTemp.CommandText = "SELECT PHONE, FAX, FIRM, ID FROM NNYBEA ORDER BY ID LIMIT " & limit_offset & "," & results_per_page
You could also use GetRows() to convert the recordset to a 2D array and apply a limit when looping
Dim r, rs_loops, theData
theData = DataRangeHdr1.getRows()
rs_loops = page_num*results_per_page
if rs_loops > uBound(theData,2) then rs_loops = uBound(theData,2)
for r = limit_offset to rs_loops
' output data from the DataRangeHdr1 recordset
%>
<tr>
<td><p align="center"><%= theData(2,r) ' firm %></td>
<td><p align="center"><%= theData(0,r) ' phone %></td>
<td><p align="center"><%= theData(1,r) ' fax %></td>
</tr>
<%
next
But this would mean storing large amounts of unseen data in memory. Using a LIMIT offset in the SQL would make more sense.

Telerik RadHtmlChart Pie Chart Error

I am attempting to use the RadHtmlChart pie chart, but I'm getting the following error:
Cannot create an object of type 'System.Nullable from its string representation 'StatCount' for the 'Y' property.
If I use the same SqlDataSource for an asp:Chart it works as expected. So my guess is that is this specific to the RadHtmlChart - but it's just a guess.
The query (MS SQL Server 2012) returns the following:
+----------------------+-----------------+
| stat | StatCount |
+----------------------+-----------------+
| Closed | 4067 |
+----------------------+-----------------+
| Pending Assignment | 1 |
+----------------------+-----------------+
| QA Ready | 600 |
+----------------------+-----------------+
| In QA | 400 |
+----------------------+-----------------+
| Assigned | 4946 |
+----------------------+-----------------+
Here's the SqlDataSource I'm using to get this data:
<asp:SqlDataSource
ID="sdsAssnmtStats"
runat="server"
ConnectionString='<%$ ConnectionStrings:DefaultConnection %>'
SelectCommand="SELECT s.stat ,cast(case s.stat when 'Pending Assignment' then count(a.clmNo) when 'Assigned' then count(a.clmNo) when 'QA Ready' then count(a.clmNo) when 'In QA' then count(a.clmNo) when 'Closed' then count(a.clmNo) else 0 end as nvarchar) as StatCount FROM assnmts a inner join assnmtStats astats on a.assnmtIdPk = astats.assnmtIdFk inner join stats s on astats.aStatId = s.statIdPk inner join repAssnmts ra on a.assnmtIdPk = ra.assnmtIdFk inner join aspnetusers anu on ra.repId = anu.Id inner join clients c on a.clientIdFk = c.clientIdPk inner join carrs on a.carrierId = carrs.carrIdPk inner join (SELECT a2.assnmtIdPk, MAX(astats2.asCrtdDt) as MaxDate FROM assnmts a2 INNER JOIN assnmtStats astats2 on a2.assnmtIdPk = astats2.assnmtIdFk GROUP BY a2.assnmtIdPk) mdt on a.assnmtIdPk = mdt.assnmtIdPk and astats.asCrtdDt = mdt.MaxDate inner join (select a3.assnmtIdPk, MAX(ra2.raCrtdDt) as MaxRepDate from assnmts a3 inner join repAssnmts ra2 on a3.assnmtIdPk = ra2.assnmtIdFk group by a3.assnmtIdPk) mrepdt on a.assnmtIdPk = mrepdt.assnmtIdPk and ra.raCrtdDt = mrepdt.MaxRepDate group by s.stat">
</asp:SqlDataSource>
Here's the RadHtmlChart:
<telerik:RadHtmlChart runat="server" ID="rhc_assnmtStats" Transitions="true" Skin="Default" DataSourceID="sdsAssnmtStats" >
<PlotArea>
<Series>
<telerik:PieSeries StartAngle="90">
<LabelsAppearance Position="OutsideEnd"></LabelsAppearance>
<SeriesItems>
<telerik:PieSeriesItem Exploded="true" Name="stat" Visible="true" VisibleInLegend="true" Y="StatCount" />
</SeriesItems>
</telerik:PieSeries>
</Series>
</PlotArea>
<Legend>
<Appearance Position="Right" Visible="true"></Appearance>
</Legend>
<ChartTitle Text="Assignment Counts by Status">
<Appearance Align="Center" Position="Top"></Appearance>
</ChartTitle>
</telerik:RadHtmlChart>
Here's the asp:Chart markup I used that works as expected:
<asp:Chart ID="assnmtStats" runat="server" DataSourceID="sdsAssnmtStats">
<Titles>
<asp:Title Font="Times New Roman, 14pt, style=Bold, Italic" Name="AssnmtStatsTitle" Text="Assignment Counts by Status"></asp:Title>
</Titles>
<Series>
<asp:Series Name="Series1" ChartType="Pie" XValueMember="stat" YValueMembers="StatCount" YValueType="Int32" Legend="assnmtStatsLegend" IsValueShownAsLabel="true"></asp:Series>
</Series>
<ChartAreas>
<asp:ChartArea Name="assnmtStatsChartArea" Area3DStyle-Enable3D="true"></asp:ChartArea>
</ChartAreas>
<Legends>
<asp:Legend Name="assnmtStatsLegend" Alignment="Center"></asp:Legend>
</Legends>
</asp:Chart>
What do I need to do to get the RadHtmlChart to work?
This is an ASP.net C# WebForms application I'm creating in Visual Studio 2017.
I received a solution from Telerik. I hope this helps anybody else who has trouble translating the demo on their site from a bar chart to a pie chart.
When data binding the chart series you need to set its DataFieldY property to point to the field name you want to use instead of populating its SeriesItems collection.
Here's the working solution:
<telerik:RadHtmlChart runat="server" ID="rhc_assnmtStats" Transitions="true" Skin="Default" DataSourceID="sdsAssnmtStats" >
<PlotArea>
<Series>
<telerik:PieSeries StartAngle="90" NameField="stat" DataFieldY="StatCount">
<LabelsAppearance Position="OutsideEnd"></LabelsAppearance>
</telerik:PieSeries>
</Series>
</PlotArea>
<Legend>
<Appearance Position="Right" Visible="true"></Appearance>
</Legend>
<ChartTitle Text="Assignment Counts by Status">
<Appearance Align="Center" Position="Top"></Appearance>
</ChartTitle>
</telerik:RadHtmlChart>

Adding a text area in table and saving it to SQL Server database

I'm using Classic ASP to add a note function to the table that is displaying rows from a database. The inserted row will save to the database saved Remarks but the following code isn't working.
<%
Dim fRemark
fRemark = Request.Form("Remarks")
Dim rsIntra,MyQryItr2
set cnIntra = Server.CreateObject("ADODB.Connection")
set MyQryItra2 = server.CreateObject ("ADODB.Recordset")
set rsIntra = Server.CreateObject("ADODB.Recordset")
MyQryItra2 = "select Remarks from [PurchaseOrderTrackInfo]"
rsIntra.Open MyQryItra,strRMSIDMcn
if rsIntra.eof then
MyQryItr2 = "insert into [PurchaseOrderTrackInfo] Remarks values N'" & fRemark & " '; "
cast(Remarks as int)
cnIntra.Execute MyQryItr2
else
rsIntra.close
set rsIntra = Nothing
set rsIntra = server.CreateObject("ADODB.Recordset")
MyQryItr2 = "UPDATE [PurchaseOrderTrackInfo] SET Remarks = N'" & fRemark & " '; where Remarks = rowID;"
end if
set rsIntra=Nothing
strConnDB= "Driver={SQL Server};Server=GB;Database=PurchaseOrderTrackInfo;UID=madfox;PWD=;"
%>
<td colspan="10" bordercolor=#3399ff bgcolor=#FFFF99 align="center">
<font face="Arabic Transparent" size="1" color="#800080"></font>
<form action=UpdatePO1.asp method=post >
<textarea name="Remarks" cols="20" rows="2" ><%=fRemark%></textarea>
<input type="submit" class="btn1" value="save" name="finish"/>
<input type="hidden" name="rowID" value="ID" />
</td>
</form>
<%
you never execute your update query. also your update statement does not seem to be valid as you are using the column Remarks as storage for the Remark and as row id. consider adding a rowid column to you table and use the following update statement
MyQryItr2 = "UPDATE [PurchaseOrderTrackInfo] SET Remarks = N'" & fRemark & " ' where rowId =" & rowID
cnIntra.Execute MyQryItr2
Since your code is vulnerabe to SQL injection, you should look up parameterized queries.

Adding GUI in VBScript [duplicate]

This question already has answers here:
Adding a GUI to VBScript
(3 answers)
Closed 7 years ago.
I have a bunch of VBScripts and I wanted to have a GUI so that I don't have to double click the actual .vbs. Is there a way or other programming languages that can launch VBS and have GUI?
This is a console menu. Right click and choose Open in Command Prompt. Only options 3 and 5 do anything. This is from Filter's menu code at https://skydrive.live.com/redir?resid=E2F0CE17A268A4FA!121
Set Arg = WScript.Arguments
set WshShell = createObject("Wscript.Shell")
Set Inp = WScript.Stdin
Set Outp = Wscript.Stdout
Showmenu
Sub ShowHelpMenu
outp.writeline " -----------------------------------------------------------------------------"
outp.writeblanklines(1)
outp.writeline " Menu"
outp.writeline " ----"
outp.writeblanklines(1)
outp.writeline " 1 Help 2 HTML Help 3 Version 4 History"
outp.writeblanklines(1)
outp.writeline " 5 Exit"
outp.writeblanklines(1)
outp.write "Filter>"
End Sub
'=============================================
Sub ShowMenu
Do
ShowHelpMenu
Answ=Inp.readline
If Answ = "1" Then
ShowGeneralHelp "TEXT"
Elseif Answ = "2" Then
ShowGeneralHelp "HTML"
Elseif Answ = "3" Then
Version
Elseif Answ = "4" Then
History
Elseif Answ = "5" Then
Exit Do
End If
Loop
End Sub
'=============================================
Sub History
On Error Resume Next
WshShell.Run """" & FilterPath & "FilterHistory.txt""" , 1, False
err.clear
End Sub
'=============================================
Sub Version
outp.writeblanklines(1)
outp.writeline " Version"
outp.writeline " -------"
outp.writeblanklines(1)
outp.writeline " Filter Ver 0.6 - 2015 (Public Domain)"
outp.writeblanklines(1)
outp.writeline " by David Candy"
outp.writeblanklines(1)
End Sub
HTA or web pages give VBSript graphical ui. The main difference is HTA avoid security prompts. Although if you load a local web page, so do web pages. You program an HTA as if it's a web page.
Here's an HTA in HTML/VBScript, It uses a object that is a text database object.
<html>
<head>
<style>
BODY {font-size :100%;font-family: Arial, Helvetica, sans-serif;color: black;
background:URL(images/watermark.gif);background-color: white;
margin-top:0; margin-left:0pt; margin-right:0pt ; text-align:Justify}
P {margin-left:40pt;margin-right:10pt}
TABLE {font-size: 90%; text-align:left; margin-left:40pt;margin-right:10pt;background-color:lavender;width:90%}
THEAD {color: white;font-weight:bold;background-color:darkblue; margin-left:40pt;margin-right:10pt}
TD {Vertical-Align:Top;padding:3px}
</style>
</head>
<body>
<OBJECT CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83"
ID=dsoMacro5 WIDTH=0 HEIGHT=0>
<PARAM NAME="DataURL" VALUE="music.txt">
<PARAM NAME="UseHeader" Value="True">
<PARAM NAME="FieldDelim" VALUE=" ">
<PARAM NAME="Sort" Value="Title">
</OBJECT>
<h3>My Music Database</h3>
<h4>Select a button to filter list</h4>
<p>To search for a word in the Title field use <i>* word *</i>. To search for the first word in a field use <i>Word *</i> or the last word use <i>* word</i>. To search for a string within a word or word use <i>*partialword*</i>. Searches are case sensitive.</i></p>
<p><INPUT Name=tb1 TYPE=Text Value=""> <INPUT ID=cmdNavFirst TYPE=BUTTON VALUE=" Search " onclick="dsoMacro5.object.filter='Title=' + tb1.value;dsoMacro5.reset()"></p>
<p><INPUT ID=cmdNavFirst TYPE=BUTTON VALUE=" Sort Book " onclick="dsoMacro5.object.sort='Book';dsoMacro5.reset()"></p>
<hr class="body">
<TABLE ID=tblMacro2 DATASRC=#dsoMacro5 OnRowEnter=Alert(tblMacro2.row)>
<THEAD>
<TR>
<TD WIDTH="20%"><b>Number</b></TD>
<TD WIDTH="60%"><b>Title</b></TD>
<TD WIDTH="20%"><b>Book</b></TD>
</TR>
</THEAD>
<TBODY>
<TR>
<TD WIDTH="20%"><SPAN DATAFLD=Number></SPAN></TD>
<TD WIDTH="60%"><SPAN DATAFLD=Title></SPAN></TD>
<TD WIDTH="20%"><SPAN DATAFLD=Book></SPAN></TD>
</TR>
</TBODY>
</TABLE>
</body>
</html>
For this to work you need a database file called music.txt. Note that is TABS between fields.
Number Title Book
1 One A song
2 Two A another song
3 Three A yet another song
4 Four Yes it's a song
The first choice for a VBScript GUI is a HTA. All languages that can create a window/dialog and call external programs can run something like
P:\athto\corwscript.exe P:\ath\to\script.vbs pa ra me ters
So stick to HTA or pick the language you are most familiar with.

Nokogiri parsing HTML

I am using Nokogiri to parse my HTML code. My HTML looks like this:
<table>
<tr>
<td>
<p>Important Preferences</p>
To see as much as possible
<br />Relaxation
<br />Quality of accommodation
<br />Quality of activities
<br />Independence & flexibility
<br />Safety & security
</td>
<td>
<p>Budget Preferences</p>
4000 to 5000 USD per person
<br />5000 to 6000 USD per person
<br />Above 6000 USD per person
</td>
</tr>
</table>
I am trying to make a hash from it, which would be like this:
{
"Important Preferences" => "To see as much as possible, Relaxation, Quality of accommodation, Quality of activities, Independence & flexibility, Safety & security",
"Budget Preferences" => "4000 to 5000 USD per person, 5000 to 6000 USD per person, Above 6000 USD per person"
}
I tried:
params = {}
Nokogiri::HTML("my HTML pls see above").css("td p").each do |item|
params.merge!({item.text => item.next.text})
end
But I couldn't collect values inside <BR>.
My result was:
{
"Important Preferences" => "To see as much as possible",
"Budget Preferences" => "4000 to 5000 USD per person"
}
At the first step find out all <td> tags with xpath('//td'). Then, for each, iterate on its children and collect its content, if the child it Nokogiri::XML::Text (you don't want to collect <br> tags):
doc = Nokogiri::HTML.parse(html)
h = {}
doc.xpath('//td').each do |td|
p = td.at_xpath('p')
a = []
td.children.each do |child|
if Nokogiri::XML::Text === child
t = child.text.strip
a << t unless t.empty?
end
end
h[p.text] = a.join(', ')
end
result:
{"Important Preferences"=>"To see as much as possible, Relaxation, Quality of accommodation, Quality of activities, Independence & flexibility, Safety & security",
"Budget Preferences"=>"4000 to 5000 USD per person, 5000 to 6000 USD per person, Above 6000 USD per person"}
or in more compressed form, without using the strict loops:
doc = Nokogiri::HTML.parse(html)
h = {}
doc.xpath('//td').each do |td|
h[td.at_xpath('p').text] = td.children
.select{|x| Nokogiri::XML::Text === x && !x.text.strip.empty?}
.map{|x| x.text.strip}.join(', ')
end
You basically want to get all siblings of td p
You can get list of all siblings and remove p.
item.parent.children.to_a - [item]
I'd do it like this:
require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<table>
<tr>
<td>
<p>Important Preferences</p>
To see as much as possible
<br />Relaxation
<br />Quality of accommodation
<br />Quality of activities
<br />Independence & flexibility
<br />Safety & security
</td>
<td>
<p>Budget Preferences</p>
4000 to 5000 USD per person
<br />5000 to 6000 USD per person
<br />Above 6000 USD per person
</td>
</tr>
</table>
EOT
doc.search('td').map { |td|
key = td.at('p').text
[
key,
td.text.sub(/#{key}/, '').lstrip.gsub(/\n +/, ', ')
]
}.to_h
# => {"Important Preferences"=>"To see as much as possible, Relaxation, Quality of accommodation, Quality of activities, Independence & flexibility, Safety & security, ", "Budget Preferences"=>"4000 to 5000 USD per person, 5000 to 6000 USD per person, Above 6000 USD per person, "}
If you're on an older version of Ruby that doesn't have to_h, use:
Hash[
doc.search('td').map { |td|
key = td.at('p').text
[
key,
td.text.sub(/#{key}/, '').lstrip.gsub(/\n +/, ', ')
]
}
]
# => {"Important Preferences"=>"To see as much as possible, Relaxation, Quality of accommodation, Quality of activities, Independence & flexibility, Safety & security, ", "Budget Preferences"=>"4000 to 5000 USD per person, 5000 to 6000 USD per person, Above 6000 USD per person, "}

Resources