I tried to improve the Tip Calculator example a bit, so that "Total: " doesn't disappear when you hit enter on the tip field:
REBOL [title: "Tip Calculator"]
view [
f: field "49.99"
hpanel [
t: field "20" on-action [
set-face x rejoin ["Total: " round/to to money! ((to-decimal get-face f) * ((to-decimal get-face t) * .01 + 1)) $.01 ]
]
title "%"
]
x: title "Total: " facets [ auto-size true ]
]
However, the auto-size doesn't seem to help the fact that x stays too skinny after calculation. This works:
x: title "Total: " 150x20
...as does this:
x: title "Total: "
...but how can I get it to resize automatically?
As title seems to descend from text, which is defined as auto-size: false, it would be the most easy solution to replace title with a style defined with auto-size: true e.g info
x: info "Total: "
Otherwise you would have to redefine your own title style descending from a autosizing style.
Related
The following segment of a JSON file needs to be transformed, essentially flattening it to a single hash for sizes
"sizes" : {
"small" : {
"w" : "680",
"h" : "261",
},
"large" : {
"w" : "878",
"h" : "337",
},
"medium" : {
"w" : "878",
"h" : "337",
}
while the child attributes are accessible as:
parent['sizes'].each do |size|
w: size['w'],
h: size['h'],
label: size
end
will not function, as the whole hash will be loaded for the label. How can the hash's identity string only be captured?
For hashes each calls the block once for each key in hash, passing the key-value pair as parameters.
Like this:
parent['sizes'].each do |key, size|
# format your preferred output here
# key is small, large, medium
# and size is {"w"=>"680", "h"=>"261"}, ...
# for example
{
w: size['w'],
h: size['h'],
label: key
}
end
I am a student working on my first CLI project with Ruby, and there is a website I am trying to scrape with Nokogiri. The contents of the website are not strictly organized into their own classes/id, but there is some information that I simply cannot figure out how to scrape.
This is what it looks like:
<p>
<strong> First Aired:</strong>
"2017 | "
<strong> Episodes:</strong>
" 24"
<br>
I want to know if there is a way to scrape the string that comes after each "Episode:" element. The code I tried was
doc = Nokogiri::HTML(open(https://www.techradar.com/best/best-anime))
doc.css('p strong')[1].text <= and that got me the "Episodes:"
then i tried:
doc.css('p strong')[1].next_element <= and it skipped the string and gave me "<br>
I also tried the .children method, but that also returned "Episodes:". I think I am confusing a lot of terms since these methods have no effect on the string. Is it even possible to grab that string with CSS? Lastly, if that were possible to grab, it there a way to grab only the strings after "Episodes:"?
I appreciate any help. I tried to do some research on Nokogiri and Css, but I think I am confusing a lot of things.
HTML is heirarchical, so for all the elements you pasted, p is the parent, and the others are its children. This is especially apparent if the HTML is properly formatted and indented.
This means that you will find the " 24" under p, like this:
html = <<~STR
<p>
<strong> First Aired:</strong>
"2017 | "
<strong> Episodes:</strong>
" 24"
<br>
STR
html_doc = Nokogiri::HTML.parse(html)
p_element = html_doc.css('p')
p_element.children.map(&:name)
# => ["text", "strong", "text", "strong", "text", "br", "text"]
p_element.children.map(&:to_s)
# => [
# "\n ",
# "<strong> First Aired:</strong>",
# "\n \"2017 | \"\n ",
# "<strong> Episodes:</strong>",
# "\n \" 24\"\n ", <------------ this is what you wanted
# "<br>",
# "\n"
# ]
p_element.children[4]
=> #(Text "\n \" 24\"\n ")
If you want the sibling element immediately after one that has "Episodes:" in it, one way is to do this:
consecutive_pairs = p_element.children.each_cons(2)
_before, after = consecutive_pairs.detect do |before, after|
before.text.include?("Episodes")
end
after
# => #(Text "\n \" 24\"\n ")
Using Elm's Html module, the output do not have any whitespace between html tags. But for inline displayed tags, whitespace has a function and makes the browser display empty space between them.
div []
[ strong [] [ text "Key:" ]
, span [] [ text "value" ]
, text "(extra)"
]
Results in:
<div><strong>Key:</strong><span>value</span>(extra)</div>
Shown in a browser as: Key:value(extra)
Desired result
The desired html must have some kind of whitespace:
<div>
<strong>Key:</strong>
<span>value</span>
(extra)
</div>
Shown in browser as: Key: value (extra)
You could just put whitespace between the elements manually:
div []
[ strong [] [ text "Key:" ]
, text " "
, span [] [ text "value" ]
, text " "
, text "(extra)"
]
Or if you want something more generic you can easily write a function to do so using List.intersperse, e.g.:
withSpacing : List (Html msg) -> List (Html msg)
withSpacing =
List.intersperse (text " ")
main : Html msg
main =
div []
(withSpacing
[ strong [] [ text "Key:" ]
, span [] [ text "value" ]
, text "(extra)"
]
)
Or with even more convenience, based on the reasonable assumption that you'll be applying it to a function that takes a list of child elements to produce a single element:
withSpacing : (List (Html msg) -> Html msg) -> List (Html msg) -> Html msg
withSpacing element =
List.intersperse (text " ") >> element
main : Html msg
main =
withSpacing (div [])
[ strong [] [ text "Key:" ]
, span [] [ text "value" ]
, text "(extra)"
]
You can make a wrapper for Html tags as follows:
type alias HtmlTag msg =
List (Html.Attribute msg) -> List (Html msg) -> Html msg
interspaced : HtmlTag msg -> HtmlTag msg
interspaced fn =
\attr content -> fn attr (content |> List.intersperse (text " "))
main : Html msg
main =
interspaced div []
[ strong [] [ text "Key:" ]
, span [] [ text "value" ]
, text "(extra)"
]
I have been playing with the spotify API in an effort to get comfortable with common Lisp. The language is a large departure from what I am used to and I am afraid I may be missing some understanding of how to parse objects and lists.
When I poll the spotify API looking for a song by title. It returns a lot of json which I access using cl-json decode method. However in order to access any of the fields I find myself doing long nested car cdr combinations. The cl-json returns not a listp or lista but a giant list of nested cons. It seems like a nightmare to parse. I am attaching the code below as an example. So what I want to ask is, What am I missing? This code looks way to cumbersome and messy to be the way its done in LISP, and I assume there is a nice way to map all the JSON to some type of object or hierarchical tree? Which would make querying easier.
(ql:quickload '(:cl-json :drakma))
(defvar *auth-token*)
(defvar *results*)
(setq *url* "https://api.spotify.com/v1/search")
(push (cons "application" "json") drakma:*text-content-types*)
(setf drakma:*header-stream* *standard-output*)
(defun search-spotify (&optional (search-param "blue moon") (search-type "track") (limit 5))
"Perform a search on the API for track, artist, or album information"
(defvar complete-url "")
(defvar url nil)
(defvar params nil)
(setq url "https://api.spotify.com/v1/search")
(setq params (format nil "?q=~a&type=~a&limit=~a" (replace-all search-param " " "+") (replace-all search-type " " "+") limit))
(setq complete-url (format nil "~a~a" url params))
(print complete-url)
(setq *results* (cl-json:decode-json-from-string
(drakma:http-request complete-url
:method :get
:additional-headers `(("Authorization" . ,(format nil "Bearer ~a" (cdar *auth-token*))))
))))
(defun print-spotify-artist ()
(loop for result in (cdr (caddar *results*))
do (format t "Artist Name: ~a Spotify ID: ~a ~%" (cdadr (cddadr (caddar result))) (cdar (cdaadr (caddar result))))))
Running the above code and calling the print-spotify-artist function will print a list as follows:
Artist Name: Eve 6 Spotify ID:
https://open.spotify.com/artist/4Eqd24yS5YcxI8b6Xfuwr8 Artist Name:
Bill Murray Spotify ID:
https://open.spotify.com/artist/3wkZ8WTrs7WcfE13voUCK1 Artist Name:
Various Artists Spotify ID:
https://open.spotify.com/artist/0LyfQWJT6nXafLPZqxe9Of Artist Name:
Pat Martino Spotify ID:
https://open.spotify.com/artist/4DlMMgnldzX6OkCskmeGKz Artist Name:
Jess & Zeb Spotify ID:
https://open.spotify.com/artist/1oAndP8vmGtTlB6mbpieJs
Example JSON return, you get this returned for each song, for this example data I set the maximum results to 1. Notice that it is heavily nested. The below is what is returned from decode json function of cl-json.
((:TRACKS
(:HREF
. "https://api.spotify.com/v1/search?query=open%2Broad%2Bsong&type=track&offset=0&limit=1")
(:ITEMS
((:ALBUM (:ALBUM--TYPE . "album")
(:ARTISTS
((:EXTERNAL--URLS
(:SPOTIFY . "https://open.spotify.com/artist/4Eqd24yS5YcxI8b6Xfuwr8"))
(:HREF . "https://api.spotify.com/v1/artists/4Eqd24yS5YcxI8b6Xfuwr8")
(:ID . "4Eqd24yS5YcxI8b6Xfuwr8") (:NAME . "Eve 6") (:TYPE . "artist")
(:URI . "spotify:artist:4Eqd24yS5YcxI8b6Xfuwr8")))
(:AVAILABLE--MARKETS "AD" "AR" "AT" "AU" "BE" "BG" "BO" "BR" "CA" "CH"
"CL" "CO" "CR" "CY" "CZ" "DE" "DK" "DO" "EC" "EE" "ES" "FI" "FR" "GB"
"GR" "GT" "HK" "HN" "HU" "ID" "IE" "IL" "IS" "IT" "JP" "LI" "LT" "LU"
"LV" "MC" "MT" "MY" "NI" "NL" "NO" "NZ" "PA" "PE" "PH" "PL" "PT" "PY"
"RO" "SE" "SG" "SK" "SV" "TH" "TR" "TW" "US" "UY" "VN" "ZA")
(:EXTERNAL--URLS
(:SPOTIFY . "https://open.spotify.com/album/1qJOmC60ez9RNWPg4ELMBW"))
(:HREF . "https://api.spotify.com/v1/albums/1qJOmC60ez9RNWPg4ELMBW")
(:ID . "1qJOmC60ez9RNWPg4ELMBW")
(:IMAGES
((:HEIGHT . 639)
(:URL
. "https://i.scdn.co/image/3f7891ffa993954dd72ed50245280b15f5db5844")
(:WIDTH . 621))
((:HEIGHT . 300)
(:URL
. "https://i.scdn.co/image/16f1a6277b827fd97cb450c5dc246d29c2ed1d52")
(:WIDTH . 291))
((:HEIGHT . 64)
(:URL
. "https://i.scdn.co/image/935aca4d861213415fc64f780b0e9e5a0e8d865c")
(:WIDTH . 62)))
(:NAME . "Eve 6") (:RELEASE--DATE . "1998")
(:RELEASE--DATE--PRECISION . "year") (:TYPE . "album")
(:URI . "spotify:album:1qJOmC60ez9RNWPg4ELMBW"))
(:ARTISTS
((:EXTERNAL--URLS
(:SPOTIFY . "https://open.spotify.com/artist/4Eqd24yS5YcxI8b6Xfuwr8"))
(:HREF . "https://api.spotify.com/v1/artists/4Eqd24yS5YcxI8b6Xfuwr8")
(:ID . "4Eqd24yS5YcxI8b6Xfuwr8") (:NAME . "Eve 6") (:TYPE . "artist")
(:URI . "spotify:artist:4Eqd24yS5YcxI8b6Xfuwr8")))
(:AVAILABLE--MARKETS "AD" "AR" "AT" "AU" "BE" "BG" "BO" "BR" "CA" "CH" "CL"
"CO" "CR" "CY" "CZ" "DE" "DK" "DO" "EC" "EE" "ES" "FI" "FR" "GB" "GR" "GT"
"HK" "HN" "HU" "ID" "IE" "IL" "IS" "IT" "JP" "LI" "LT" "LU" "LV" "MC" "MT"
"MY" "NI" "NL" "NO" "NZ" "PA" "PE" "PH" "PL" "PT" "PY" "RO" "SE" "SG" "SK"
"SV" "TH" "TR" "TW" "US" "UY" "VN" "ZA")
(:DISC--NUMBER . 1) (:DURATION--MS . 194866) (:EXPLICIT)
(:EXTERNAL--IDS (:ISRC . "USRC19806851"))
(:EXTERNAL--URLS
(:SPOTIFY . "https://open.spotify.com/track/7kAKO1EYHt2MVlombUuoLN"))
(:HREF . "https://api.spotify.com/v1/tracks/7kAKO1EYHt2MVlombUuoLN")
(:ID . "7kAKO1EYHt2MVlombUuoLN") (:IS--LOCAL) (:NAME . "Open Road Song")
(:POPULARITY . 44)
(:PREVIEW--URL
. "https://p.scdn.co/mp3-preview/b793285aeeb4a1176b93dc739ffb361c6aabf4e5?cid=b067abcbc67f4ceba0d61e414926c9f5")
(:TRACK--NUMBER . 5) (:TYPE . "track")
(:URI . "spotify:track:7kAKO1EYHt2MVlombUuoLN")))
(:LIMIT . 1)
(:NEXT
. "https://api.spotify.com/v1/search?query=open%2Broad%2Bsong&type=track&offset=1&limit=1")
(:OFFSET . 0) (:PREVIOUS) (:TOTAL . 43)))
The Access library might help, it allows to access nested data structures.
(defparameter my-plist (list :foo "foo" :bar "bar"))
;; bar is a plist
(defclass obj-test ()
((foo :accessor foo :initarg :foo :initform :foo)
(bar :accessor bar :initarg :bar :initform (copy-list MY-PLIST))))
(defvar my-obj ((make-instance 'obj-test))
(accesses MY-OBJ 'bar 'foo) ;; => "foo"
There is a way to use dotted paths, if that is to your liking.
Access is battle tested, it is the heart of the Djula templating engine, one of the most downloaded libraries on Quicklisp.
Access also allows consistent access across data structures.
Deeper overview: https://lisp-journey.gitlab.io/blog/generice-consistent-access-of-data-structures-dotted-path/
I had asked the same question after editing 2 times of a previous question I had posted. I am sorry for the bad usage of this website. I have flagged it for deletion and I am posting a proper new question on the same here. Please look into this.
I am basically working on a recommender system code. The output has to be converted to sequence of JSON objects. I have a matrix that has a look up table for every item ID, with the list of the closest items it is related to and the the similarity scores associated with their combinations.
Let me explain through a example.
Suppose I have a matrix
In the below example, Item 1 is similar to Items 22 and 23 with similarity scores 0.8 and 0.5 respectively. And the remaining rows follow the same structure.
X1 X2 X3 X4 X5
1 22 23 0.8 0.5
34 4 87 0.4 0.4
23 7 92 0.6 0.5
I want a JSON structure for every item (every X1 for every row) along with the recommended items and the similarity scores for each combination as a separate JSON entity and this being done in sequence. I don't want an entire JSON object containing these individual ones.
Assume there is one more entity called "coid" that will be given as input to the code. I assume it is XYZ and it is same for all the rows.
{ "_id" : { "coid" : "XYZ", "iid" : "1"}, "items" : [ { "item" : "22", "score" : 0.8},{ "item": "23", "score" : 0.5}] }
{ "_id" : { "coid" : "XYZ", "iid" : "34"},"items" : [ { "item" : "4", "score" : 0.4},{ "item": "87", "score" : 0.4}] }
{ "_id" : { "coid" : "XYZ", "iid" : "23"},"items" : [ { "item" : "7", "score" : 0.6},{ "item": "92", "score" : 0.5}] }
As in the above, each entity is a valid JSON structure/object but they are not put together into a separate JSON object as a whole.
I appreciate all the help done for the previous question but somehow I feel this new alteration I have here is not related to them because in the end, if you do a toJSON(some entity), then it converts the entire thing to one JSON object. I don't want that.
I want individual ones like these to be written to a file.
I am very sorry for my ignorance and inconvenience. Please help.
Thanks.
library(rjson)
## Your matrix
mat <- matrix(c(1,34,23,
22, 4, 7,
23,87,92,
0.8, 0.4, 0.6,
0.5, 0.4, 0.5), byrow=FALSE, nrow=3)
I use a function (not very interesting name makejson) that takes a row of the matrix and returns a JSON object. It makes two list objects, _id and items, and combines them to a JSON object
makejson <- function(x, coid="ABC") {
`_id` <- list(coid = coid, iid=x[1])
nitem <- (length(x) - 1) / 2 # Number of items
items <- list()
for(i in seq(1, nitem)) {
items[[i]] <- list(item = x[i + 1], score = x[i + 1 + nitem])
}
toJSON(list(`_id`=`_id`, items=items))
}
Then using apply (or a for loop) I use the function for each row of the matrix.
res <- apply(mat, 1, makejson, coid="XYZ")
cat(res, sep = "\n")
## {"_id":{"coid":"XYZ","iid":1},"items":[{"item":22,"score":0.8},{"item":23,"score":0.5}]}
## {"_id":{"coid":"XYZ","iid":34},"items":[{"item":4,"score":0.4},{"item":87,"score":0.4}]}
## {"_id":{"coid":"XYZ","iid":23},"items":[{"item":7,"score":0.6},{"item":92,"score":0.5}]}
The result can be saved to a file with cat by specifying the file argument.
## cat(res, sep="\n", file="out.json")
There is a small difference in your output and mine, the numbers are in quotes ("). If you want to have it like that, mat has to be character.
## mat <- matrix(as.character(c(1,34,23, ...
Hope it helps,
alex