Split string based on string length in dotliquid - azure-logic-apps

I have a scenario where I need to split a string say "VIP tickets John Smith concert today at 4pm and later dinner" into parts where each part is upto 35 chars long. I can see for and slice but this has to be done in a loop to create following json
{
"Informations": [
{
"Information": "VIP tickets John Smith concert toda"
},
{
"Information": "y at 4pm and later dinner"
}
]
}
how should I slice it? I am trying to write liquid transformation for logic app
{
"Informations": [
{
"Information": "VIP tickets John Smith concert toda"
},
{
"Information": "y at 4pm and later dinner"
}
]
}

Using Slice expression and compose action, you can split string into multiple words and form the Json. Below are steps I followed,
Designer of logic app can be shown below,
In Initialize variable action, Initialized string variable with value "VIP tickets John Smith concert today at 4pm and later dinner"
In second Initialize variable action, splitting string variable with expression slice(variables('string var'),0,35). This will split string into another string with length of 35.
In third Initialize variable action, splitting string variable with expression
slice(variables('string var'),35,70).This will split string into another string with length of 35.
In compose action forming array with above two sliced strings,
Next taken loop and passing input from compose output. Using another compose action in loop as shown below,
At last using another compose action to form required json as show below,
Below is output of logic app,
Output of Initialize variable 2&3
Output of last compose action,

Related

Swift - JSON Decode into Struct Contains Against String

I have a JSON file I am grabbing from a remote server and decoding it. It is being decoded into a struct and then used in an #Published var.
For example:
JSON:
[{"keyword": "foo"}, {"keyword": "blah"}]
Struct:
struct keywords: Codable {
var keyword: String
}
Observable Class:
#Published var keys: [keywords] = []
I need to do a real-time comparison, using contains, against a value the user is entering, utilizing an if statement. I can get it to use the first entry in the keys var and check against any characters that might be in that string, but I cannot get it to work across the entire array on only the full strings (not individual characters).
Here's what correctly checks against only the first entry of the array and each individual character.
if keys[0].keyword.contains(where: blah.contains)
I have also tried mapping it to strings like this (does exactly the same thing):
if (keys[0].keyword.map{String($0)}).contains(where: blah.contains)
Been at this all day but cannot find any docs on how to do this correctly.
The goal is to not just have it use the first entry of the array but the entirety of the entries of the array. I understand that is the [0] but it wouldn't compile without it. I need to make it compare on the entire string, not on individual characters.
For example, if the word blah is the array and the user enters bla it should not match anything. Only if the entire word blah is contained in the user's entry should it match (i.e. fooblahfoo would match because blah is within the string, but foobalbabh would NOT match even though all of the characters contained within the word blah are in that string).
Appreciate any assistance with actual code examples. This is Swift 5.5.
UPDATE:
Not sure where the confusion is coming from but here's an even clear explanation.
There is a TextField called username where the user enters a string.
If the user enters the string fooblahfoo and the word blah is somewhere in the array it should match.
This is easy when you have a simple array of strings like this:
keywords = ["foo", "blah"]
For that you just do keywords.contains(where: username.contains)
However, the decoded JSON is more of a dictionary than a simple array of strings, so you have to call to the key of keyword and look at all the values in order to make the comparison.
First, use contains(where:) on the keys array -- then, check each item to see if the user input contains that keyword.
struct Keywords: Codable {
var keyword: String
}
var keys: [Keywords] = [.init(keyword: "foo"),.init(keyword: "blah")]
var userInput = "fooblahfoo"
var result = keys.contains { userInput.contains($0.keyword) }
print(result)
true

Swift Array multiple appending on click

I'm creating a button that when clicked adds the current date and time to an array but when I try to append to the array it only appends once and not repeating the process
the Entries struct:
struct Enteries {
var dates:[String] = []
}
convert date to String:
func DateConverter(){
format.timeZone = .current
format.dateFormat = "yyyy-MM-dd HH:mm"
dateString = format.string(from: currentDate)
}
The function that appends: also its called later whenever an IBAction is triggered
func AddToDatabase () {
var entery = Enteries()
entery.dates.append(dateString)
print(entery.dates)
}
`
Yikes, there's a lot going on here.
First of all, Swift's convention is to use lowerCamelCase for functions. Only type names should be UpperCamelCase.
Secondly, function names should be verbs or verb phrases, type names should be nouns. If I saw DateConverter in some code, I would expect it to be a type. It's an UpperCamelCase noun, that's how types should be named. But yours is a function (which would be a total surprise to every other Swift developer, because it violates the expectations they've built up from Swift's naming conventions), that function should probably be called parseDate.
Which way does DateConverter convert? From String to Date, Date to String, or both? What's its input? What's it's output? These things should be obvious from a good function name, but are totally unknown here without looking at the implementation.
Critically, the DateConverter function doesn't take input from parameters, and doesn't return a result, instead it takes input from a side effect (accessing the variable currentDate) and returns a result via side effect (writing to an a variable dateString). This is really bad, for several reasons:
It's not reusable. You have no way to use this date parsing code somewhere else without copy/pasting it, which is how code duplication and complexity arise. If you ever decide to change the date format in your app, you won't have a central source-of-truth that you can change, instead you'll have to manually hunt down every copy of this function, and change it, hoping you don't miss any. Not good.
It's not thread safe
It's more complex than a simple function that has type (Date) -> String. It obfuscates what's going on.
It defies peoples' expectations, without justification.
Enteries.dates has a default value of [], which doesn't seem to be a good idea if you're going to be appending to it as soon as you create it. Instead, take the array via an initializer parameter.
Enteries.dates has type [String]. Why?! You already have Date objects, store those!
They're smaller (in memory)
They're presentation-agnostic, meaning you can properly format them for different interfaces and different locales at a later time, as necessary
They support date math. I often see people storing dates as strings, and ask questions like "How do I sort my array of dates?" (which are actually stored as strings), "How do I add 1 day to "2019-12-24", and they start doing funky parsing, splitting, joining, and it's all just an absolute mess
Here's how I would improve this code:
struct Entries {
var entries: [Entry]
}
struct Entry {
let date: Date
}
// Call this from your view layer, only when you're about to present a `Date` to a user.
func parse(date: Date) -> String {
let df = DateFormatter()
df.timeZone = .current
df.dateFormat = "yyyy-MM-dd HH:mm"
return format.string(from: currentDate)
}
var entries = Entries(entries: [])
func addToDatabase(entry: Entry) {
entries.append(entry)
print(enteries.entries)
}
you are creating a new entery object eveytime the function is called. SO its creating a new object everytime. Declare your entery object outside the function.
var entery = Enteries()
func AddToDatabase () {
entery.dates.append(dateString)
print(entery.dates)
}

Why does map function mutate array of objects when operating on each element's attribute?

I have an array of objects:
class Person
attr_accessor :email
def initialize(email)
#email = email
end
end
array = [
Person.new('hello#gmail.com'),
Person.new('world#gmail.com')
]
I created a clone from the original array to perform map function, and then I mapped over each element to make its email attribute become uppercase:
clone = array.clone
clone.map { |obj|
obj.email.upcase!
obj
}
puts array.inspect # why is the original being mutated
puts clone.inspect
It mutates the original array. I have experimented with both dup and clone. and I get the same result. Why does map mutate the objects when operating on each element's attribute?
You cloned the array containing Person references, but you did not change the array; you changed the Person instances themselves. clone is so-called "shallow clone", which copies only the receiver object, but none of the objects whose references it may contain.
In real-world logic: you took a piece of paper on which you wrote "Jenny, Timmy". Then you copied it to another piece of paper. You then took the first piece of paper, found the people it refered to, and gave them an apple. Then you took the second piece of paper, found the people on it, and wondered where their apples came from. But there's only one Timmy, only one Jenny: you give the first list's Jenny an apple, the second list's Jenny also has one.
If you want to clone something, clone Jenny.
array.map { |person|
person.clone.yield_self { |clone|
clone.email = clone.email.upcase
}
}
(Note that I didn't use clone.email.upcase!. The reason is the same reason all over again: if you clone an object, they will both use the same string for email. upcase! changes that string, which would uppercase both clone's email and the original's email. Thus, we make a new email string for the clone.)
Things like this are best understood by stepping through the visualisation using this tool. However, the tool runs Ruby 2.2, which doesn't know about yield_self; this code is equivalent:
array.map { |person|
clone = person.clone
clone.email = clone.email.upcase
clone
}
You could also write this, though it won't visualise as clearly:
array.map(&:clone).map { |clone|
clone.email = clone.email.upcase
}

proper way to get the last element of the array when split is used on the string within JSON

I have a JSON response from the server and I am using map to use only necessary key:valuepairs in Angular (typescript) that will be used to display on the Frontend side.
here bizStep is actually according to a standard (EPCIS) and has the following value:
urn:epcglobal:cbv:bizstep:receiving
I only want to the user to read receiving hence I used split and obtained the last value of the array to display the value.
The logic is shown below:
this.serv.getEpcisInfo(code) // HTTP GET Service from Angular
.subscribe(res => {
this.data = res.map(el => { // map only some key value pairs now!
return {
'business step': el.bizStep.split(':')[el.bizStep.split(':').length - 1]
});
});
But it is observed that in order to obtain the overall length of the splited string array I have to write the expression el.bizStep.split(':') twice.
Is there a shorthand or elegant expression to obtain the last string value of the array.
I did try to use el.bizStep.split(':')[-1] however this expression failed and did not provide me any value.
You can use Array.pop since you don't need to preserve the result of the split, i.e. el.bizStep.split(':').pop().
A more general approach would be to use an anonymous function, e.g.:
(s => s[s.length-1])(el.bizStep.split(':'))
You could modify this to get elements other than the last. Of course, this example has no error checking on the length or type of el.bizStep.

Alexa custom slot that takes any word or phrase

What samples can one add to a custom slot to make it accept any word or phrase?
Update
This solution has been outdated with the introduction of phrase slots eg. AMAZON.SearchQuery.
From the Announcements
Phrase slots are designed to improve speech recognition accuracy for
skills where you cannot include a majority of possible values in the
slot while creating the interaction model. The first slot available in
this category is AMAZON.SearchQuery is designed to provide improved
ability for you to collect generic speech from users.
The Problem
Having worked on developing an urban dictionary skill over the weekend to polish up on my Alexa skills I ran into a problem which I think a lot of skill developers might encounter.
TL;DR
Namely, how do you train Alexa on a custom slot to be able to take any value you give it?
First Attempts
At first I added about 5 words to the custom slot samples like bae, boo, ship it. But I quickly found that the skill would only work with those 5 words and I'd get no calls to my lambda function for words outside that list.
I then used
from nltk.corpus import words
import json, random
words_list = random.shuffle(words.words()[:1000])
words_list = [word.lower() for word in words_list]
words_list = list(set(words_list))
values = []
for word in words_list:
value = {}
value['id'] = None
value['name'] = {}
value['name']['value'] = word
value['name']['synonyms'] = []
values.append(value)
print(json.dumps(values))
The above code uses nltk, which you can install with pip install nltk, to generate a 1000 words according to the schema you can find under code editor, it produce a thousand of these;
{
"id": null,
"name": {
"value": "amblygeusia",
"synonyms": []
}
}
I copied and pasted these under values, you can find the whole file under Code Editor on the Skills Builder page.
"languageModel": {
"types": [
{
"name": "phrase", //my custom slot's name
"values": [...] //pasted the thousand words generated here
...
After saving and building in the Skills Builder UI. This only allowed my skill to capture single word slot values. I tried generating 10 000 words in the same way and adding them as samples for the custom slot but two word and three words phrases weren't recognised and the skill failed to get the definition of phrases like;
ship it
The Solution;
What worked for me and worked really well was to generate two word samples. Despite all the examples being two worded, the skill was then able to recognise single word values and even three word values.
Here's the code to do that using nltk;
from nltk.corpus import words
import json, random
words_list = random.shuffle(words.words()[:1000])
words_list = [word.lower() for word in words_list]
words_list = list(set(words_list))
word_pairs = []
for word in words_list:
pair = ' '.join(random.sample(words_list, 2))
word_pairs.append(pair)
word_pairs = list(set(word_pairs))
for pair in word_pairs:
value = {}
value['id'] = None
value['name'] = {}
value['name']['value'] = pair
value['name']['synonyms'] = []
values.append(value)
print(json.dumps(values))
I put that in a file called custom_slot_value_geneator.py and ran it with;
python3 custom_slot_value_geneator.py | xclip -selection c
This generates the values and copies them to the clipboard.
I then copied them into the Code Editor under values, replacing the old values
"languageModel": {
"types": [
{
"name": "phrase", //my custom slot's name
"values": [...] //pasted the thousand two word pairss generated here
...
Save & Build.
That's it! Your skill would then be able to recognize any word or phrase for your custom slot, whether or not it's in the sample you generated!

Resources