If I have an array of employees, how can I sorted based on employee last name?
Should be something like:
employees sortBy: [:a :b | a lastName > b lastName]
If we make these assumptions:
The Array instance is held in a variable named employees
The Array holds a collection of instances that all respond to the message lastName by returning a String instance
You want to sort the collection in ascending order
Then you can get the job done with the following code fragment:
employees asSortedCollection: [ :a :b | a lastName < b lastName ]
This code sends the asSortedCollection: keyword message to the Array instance named employees. It passes in the Block instance, delimited by the square brackets, as the parameter to that keyword message. The Block passed in has two arguments that are named a and b and are marked by the preceding colon character all before the | character. The code within the Block after the | character will then be used to sort all the elements from the employees Array and add them to a new instance of the class SortedCollection.
Note, though, that this code ends up returning a new collection that holds the same items also held by employees, but now in the desired order. In fact, that new collection holds onto the sort criteria (the Block instance that was used as the parameter to the asSortedCollection: message) and as you add more instances to that collection in the future they will automatically be inserted in the correct sort order.
Related
I have a code like this in Chef
{
'home/user1/folder/file.erb'=>'/home/user1/folder/file',
'home/user2/folder/file.erb'=>'/home/user2/folder/file',
'home/user3/folder/file.erb'=>'/home/user3/folder/file',
'home/user4/folder/file.erb'=>'/home/user4/folder/file',
}.each do |s,d|
template d do
source s
owner user
group user
mode '600'
end
end
How do I replace value of owner and group with user1, user2, user3... from variable d?
Thanks!
Split Your Hash Values on /
There are certainly other ways to do this, but given your example an easy trick is simply to grab the user's directory from each hash value into a block-local variable at the top of each loop, which you can then reuse as needed. For example:
{
'home/user1/folder/file.erb' => '/home/user1/folder/file',
'home/user2/folder/file.erb' => '/home/user2/folder/file',
'home/user3/folder/file.erb' => '/home/user3/folder/file',
'home/user4/folder/file.erb' => '/home/user4/folder/file',
}.each do |src, dst|
# capture username for use as owner & group
usr = dst.split(?/)[2]
template dest do
source src
owner usr
group usr
mode '600'
end
end
Using String#split works by breaking the string into an Array of elements using / as a separator. Indexing into the array with [2] gives you the third element, which is the username, which you are apparently also using for the group.
The fact that it's the third element rather than the second isn't intuitive. However, when you use #split on your sample code, you get results like this:
'/home/user4/folder/file'.split ?/
#=> ["", "home", "user4", "folder", "file"]
Because of the way #split works, your inputs will yield an empty string as the first element of each destination array. Since Ruby arrays are zero-indexed, the element you want is the third one (e.g. [2]) in each of your sample values.
There are certainly other ways to do this, but this is a simple way to do what you want without making significant changes to your code. It often helps to remember that Chef (and Puppet!) are really just DSLs built on top of Ruby, so you can often use standard Ruby methods to get the job done.
I have a very large dataset with over 1000 columns, with column names formatted like this:
WORLDDATA.table2_usa_2017_population
WORLDDATA.table2_japan_2017_gnp
I only need to keep a subset of these parameters for a select few countries. I specify the custom lists:
%let list1 = usa canada uk japan southafrica;
%let list2 = population crimerate gnp;
How do I do a double for loop like so:
param_list = []
for (i in list1) {
for (j in list2) {
param_name = WORLDDATA.table2_{list1[i]}_2017_{list2[j]}
param_list.append(param_name)
}
}
such that I can use param_list in
data final_dataset;
set WORLDDATA.table2;
keep {param_list};
run;
Thank you!
Your original data set has data items country and topic encoded into the column name (metadata) you will probably need to transpose the data for use in SAS procedure steps that would use statements such as where, by and class.
Proc TRANSPOSE can pivot data from wide to tall and the output will have a column named _NAME_ which can be used in a where=(where-statement) option on the output data set. Th where-statement would be a regex expression having your lists specified as alternation (|) items in a group (such as (item-1|...|item-N)). The regex engine would perform the implicit outer join that the nested loop in the question pseudo code does. The regex pattern would use the /ix modifiers in order to have a pattern formatted for human readability that also ignores case.
In order to have Proc TRANSPOSE pivot each row of a data set, the data set needs to have row key (a variable or variables in combination) that are distinct from row to row.
Untested example:
proc transpose data=have_wide out=want_subset_categorical (where=(
prxmatch("(?ix)/
table2_
%sysfunc(translate(&LIST1.,|,%str( )) (?# list 1 spaces converted to | ors )
_2017_
%sysfunc(translate(&LIST2.,|,%str( )) (?# list 2 spaces converted to | ors )
/",_name_)
));
by <row-key>;
run;
I would like to retrieve large SQL dump between date ranges. For the same, I constructed a loop over a date list, which intends to extract adjacent fields. Unfortunately, in my case, it doesnt work as planned.
Following is my flow:
Replace Text: Takes flowfile content date list as all_first_dates
Initialize Count:
While Loop:
Get first and adjacent dates:
However, on seeing the queue, I get the first and second as this:
Whereas, I desired as 2016-01-01 and 2016-01-02 for first and second respectively on my first iteration and so on.
check the description of the getDelimitedField function and it's parameters:
Description: Parses the Subject as a delimited line of text and returns just a single field from that delimited text.
Arguments:
index: The index of the field to return. A value of 1 will return the first field, a value of 2 will return the second field, and so on.
delimiter: Optional argument that provides the character to use as a field separator. If not specified, a comma will be used. This value must be exactly 1 character.
...
you are not passing the second parameter, so the coma used to split the subject, and you got the whole subject as one element in result.
I'm creating a function that displays a lot of variables with the format Variable + Variable Name.
Define LibPub out(list)=
Func
Local X
for x,1,dim(list)
list[x]->name // How can I get the variable name here?
Disp name+list[x]
EndFor
Return 1
EndFunc
Given a list value, there is no way to find its name.
Consider this example:
a:={1,2,3,4}
b:=a ; this stores {1,2,3,4} in b
out(b)
Line 1: First the value {1,2,3,4} is created. Then an variable with name a is created and its value is set to {1,2,3,4}.
Line 2: The expression a is evaluated; the result is {1,2,3,4}. A new variable with the name b is created and its value is set to `{1,2,3,4}.
Line 3: The expression b is evaluated. The variable reference looks up what value is stored in b. The result is {1,2,3,4}. This value is then passed to the function out.
The function out receives the value {1,2,3,4}. Given the value, there is no way of knowing whether the value happened to be stored in a variable. Here the value is stored in both a and b.
However we can also look at out({1,1,1,1}+{0,2,3,4}).
The system will evaluate {1,1,1,1}+{0,2,3,4} and get {1,2,3,4}. Then out is called. The value out received the result of an expression, but an equivalent value happens to be stored in a and b. This means that the values doesn't have a name.
In general: Variables have a name and a value. Values don't have names.
If you need to print a name, then look into strings.
This will be memory intensive, but you could keep a string of variable names, and separate each name by some number of characters and get a substring based on the index of the variable in the list that you want to get. For instance, say you want to access index zero, then you take a substring starting at (index of variable * length of variable name, indexofvariable *length + length+1).
The string will be like this: say you had the variables foo, bas, random, impetus
the string will be stored like so: "foo bas random impetus "
I am writing a script to convert JSON data to an ordered CSV spreadsheet.
The JSON data itself does not necessarily contain all keys (some fields in the spreadsheet should say "NA").
Typical JSON data looks like this:
json = {"ReferringUrl":"N","PubEndDate":"2010/05/30","ItmId":"347628959","ParentItemId":"46999"}
I have a list of the keys found in each column of the spreadsheet:
keys = ["ReferringUrl", "PubEndDate", "ItmId", "ParentItemId", "OtherKey", "Etc"]
My thought was that I could iterate through each line of JSON like this:
parsed = JSON.parse(json)
result = (0..keys.length).map{ |i| parsed[keys[i]] || 'NA'} #add values associated with keys to an array, using NA if no value is present
CSV.open('file.csv', 'wb') do |csv|
csv << keys #create headings on spreadsheet
csv << result #load data associated with headings into the next line
end
Ideally, this would create a CSV file with the proper information in the proper order in a spreadsheet. However, what happens is the result data comes in completely out of order, and contains an extra column that I don't know what to do with.
Looking at the actual data, since there are actually about 100 keys and most of the fields contain NA, it is very difficult to determine what is happening.
Any advice?
The extra column comes from 0..keys.length which includes the end of the range. The last value of result is going to be parsed[keys[keys.length]] i.e. parsed[nil] i.e. nil. You can avoid that entirely by mapping keys directly
result = keys.map { |key| parsed.fetch(key, 'NA') }
As for the random order of the values, I suspect you aren't giving us all of the relevant information, because I tested your code and the result came out in the same order as keys.
Range has two possible notations
..
and
...
... is exclusive, meaning the range (A...B) would be not include B.
Change to
result = (0...keys.length).map{ |i| parsed[keys[i]] || 'NA'} #add values associated with keys to an array, using NA if no value is present
And see if that prevents the last value in that range from evaluating to nil.