I need some help with my foreach loop logic:
I need to put a comma after each print statement except for the last statement in the loop
ex: blah,blah2,blahlast
The following code puts comma even after the last statement.
foreach row $regions {
set name [lindex $row 0]
set id [lindex $row 1]
puts "{'name':'$name', 'val':'$region_id'}"
puts ","
}
Maybe if i count the number of times its going to loop, I can probably check for an if condition to put comma unlti its the last iteration
Another way to approach it is to build a list of items to print and join it at the end:
set lines {}
foreach row $regions {
set name [lindex $row 0]
set region_id [lindex $row 1] ;# changed this from id to region_id, seemed right
lappend lines "{'name':'$name', 'val':'$region_id'}"
}
puts [join $lines ",\n"]
The trick is to print the comma on all the iterations but the first one (that is, to reverse your logic):
set xs {a b c}
set s ""
set need_comma false
foreach x $xs {
if {$need_comma} {
append s ,
} else {
set need_comma true
}
append s $x
}
puts $s
would produce
a,b,c
Note that it seems you're merely looking for the join command.
I voted +1 for RHSeeger because that is what I would do. However, it seems you are trying to convert a TCL list to Python's list. If that is the case, you don't even have to worry about suppressing the last comma: Python allows that:
>>> li = [1,2,3,]
>>> len(li)
3
That means, your current solution works. Don't worry about the last comma.
Related
I'm working on a short homomorphic query which aims to transform certain letters of a string input, into other fixed letters.
For instance, I would like all the letters 'A' to be transformed in 'E', and all the 'E' to be turned into 'O'.
I can't use sequentials native replace() functions, because the following would happen:
Input : LEA
replace n°1: LEE
replace n°2: LOO
Wished output versus output obtained : LOE / LOO
So I decided to process letter by letter, looping over my string caracters. In the below example I transform all the 'E' by 'O':
MATCH (...stringToReplace..)
UNWIND range(0,size(apoc.text.split(stringToReplace,'',0))-1) AS i
SET stringToReplace = CASE
WHEN apoc.text.split(stringToReplace,'',0)[i] = 'E'
THEN substring(stringToReplace,0,i) + "O" + substring(stringToReplace, i+1, size(apoc.text.split(stringToReplace,'',0))-1)
ELSE stringToReplace
END
RETURN stringToReplace
The problem I encounter is that I'll have as many SET queries as the string has letters. I think that performance-wise, this is pretty lame.
What I would like to have, and I'm not sure it's possible in Cypher, is to modify an aggregating variable inside the loop and then SET my data. I tried to use a WITH statement before my UNWIND loop but didn't manage to store data inside a var.
Edit: I managed to do a different implementation but it's still return and setting too many times. Even though the end result is right.
MATCH (...stringToReplace...)
UNWIND range(0,size(apoc.text.split(stringToReplace,'',0))-1) AS i
WITH CASE
WHEN apoc.text.split(stringToReplace,'',0)[i] = 'a'
THEN substring(stringToReplace,0,i) + "i" + substring(stringToReplace, i+1, size(apoc.text.split(stringToReplace,'',0))-1)
ELSE stringToReplace
END AS outputString, stringToReplace
SET stringToReplace = outputString
RETURN stringToReplace
This should convert every character in stringToReplace
MATCH (...stringToReplace..)
RETURN REDUCE(s = '', c IN split(stringToReplace,'') |
s + CASE c
WHEN 'A' THEN 'E'
WHEN 'E' THEN 'O'
ELSE c
END
) as result
Just add more WHEN/THEN clauses to handle all the character conversions needed.
I have this line
week[1].inject{ |sum, jog| jog.distance }
Which gets the total distance run in week[1], which is an array of Jog records. This works when there are multiple records, but if there is only one for that week, inject simply returns the record itself, not its distance..
Is there any way for inject to work on arrays of single items, the same as larger arrays?
From the fine manual:
inject(initial, sym) → obj
inject(sym) → obj
inject(initial) { |memo, obj| block } → obj
inject { |memo, obj| block } → obj
[...]
If you do not explicitly specify an initial value for memo, then the first element of collection is used as the initial value of memo.
So if a is a one element array then:
a.inject { anything_at_all }
is equivalent to
a.first
This behavior is really just a shortcut to make things like:
a.inject(:+)
and such quick and easy to say.
If you need your block to always run then you just have to supply the initial value and make sure your block does what it needs to do:
week[1].inject(0) { |sum, jog| sum + jog.distance }
# -------------^ ^^^^^
# -----------------------------^^^^^
Without the sum + in the block you'll just end up with week[1].distance (assuming you've supplied the 0 initial value of course).
You could also say something like:
week[1].map(&:distance).inject(:+)
week[1].map(&:distance).sum
to solve the problem another way. You could also do it in the database if you don't need the individual records.
I have this code
notebooks.inject([]) do |res, nb|
res << nb.guid if Recipe::NOTEBOOKS.include?(nb.name)
end
The first nb has matches the condition and res looks like this
["xxx1234"]
The second nb does not match the condition which then delete/clears res
nil
From my understanding, the first value should remain in the array.
I'm also assigning this to a variable and want it to be a one liner.
inject works a little differently from how you're imagining. It simply returns the last return value of the loop as it loops through each item. An easy way to fix this is:
notebooks.inject([]) do |res, nb|
res << nb.guid if Recipe::NOTEBOOKS.include?(nb.name)
res # Returns the res array
end
That said, you should probably use select for your use case as you seem to be just filtering down which set of notebooks you want.. That is:
notebooks.select{|nb| Recipe::NOTEBOOKS.include?(nb.name)}.map(&:guid)
Generally, I've used inject when I need to run math on a group of items. e.g.
[1,2,3,4].inject(0) {|res, x| x * 2 + res}
If you're open to two loops, but cleaner and still one-liner:
notebooks.select { |nb| Recipe::NOTEBOOKS.include?(nb.name) }.map(&:guid)
The accumulator must be returned on each loop iteration:
notebooks.inject([]) do |res, nb|
Recipe::NOTEBOOKS.include?(nb.name) ? res << nb.guid : res
end
Actually, on each subsequent loop iteration, the accumulator passed to res block parameter is exactly what was returned from the previous iteration.
In your example, on the second iteration if returns false and
res << nb.guid if Recipe::NOTEBOOKS.include?(nb.name)
line is not executed at all. That said, after the second iteration, the accumulator takes a brand new value, that is apparently nil.
I have an SPSS variable containing lines like:
|2|3|4|5|6|7|8|10|11|12|13|14|15|16|18|20|21|22|23|24|25|26|27|28|29|
Every line starts with pipe, and ends with one. I need to refactor it into boolean variables as the following:
var var1 var2 var3 var4 var5
|2|4|5| 0 1 0 1 1
I have tried to do it with a loop like:
loop # = 1 to 72.
compute var# = SUBSTR(var,2#,1).
end loop.
exe.
My code won't work with 2 or more digits long numbers and also it won't place the values into their respective variables, so I've tried nest the char.substr(var,char.rindex(var,'|') + 1) into another loop with no luck because it still won't allow me to recognize the variable number.
How can I do it?
This looks like a nice job for the DO REPEAT command. However the type conversion is somewhat tricky:
DO REPEAT var#i=var1 TO var72
/i=1 TO 72.
COMPUTE var#i = CHAR.INDEX(var,CONCAT("|",LTRIM(STRING(i,F2.0)),"|"))>0).
END REPEAT.
Explanation: Let's go from the inside to the outside:
STRING(value,F2.0) converts the numeric values into a string of two digits (with a leading white space where the number consist of just one digit), e.g. 2 -> " 2".
LTRIM() removes the leading whitespaces, e.g. " 2" -> "2".
CONCAT() concatenates strings. In the above code it adds the "|" before and after the number, e.g. "2" -> "|2|"
CHAR.INDEX(stringvar,searchstring) returns the position at which the searchstring was found. It returns 0 if the searchstring wasn't found.
CHAR.INDEX(stringvar,searchstring)>0 returns a boolean value indicating if the searchstring was found or not.
It's easier to do the manipulations in Python than native SPSS syntax.
You can use SPSSINC TRANS extension for this purpose.
/* Example data*/.
data list free / TextStr (a99).
begin data.
"|2|3|4|5|6|7|8|10|11|12|13|14|15|16|18|20|21|22|23|24|25|26|27|28|29|"
end data.
/* defining function to achieve task */.
begin program.
def runTask(x):
numbers=map(int,filter(None,[i.strip() for i in x.lstrip('|').split("|")]))
answer=[1 if i in numbers else 0 for i in xrange(1,max(numbers)+1)]
return answer
end program.
/* Run job*/.
spssinc trans result = V1 to V30 type=0 /formula "runTask(TextStr)".
exe.
Usually for getting table size, the standard table library function # operator works.
However when I make a table which has a string key index, it doesn't work.
local function addWriterIdListToTable()
local returnTable = {}
local requestString = "1234:16 5678:8 9012:1"
local idList = requestString:split(" ")
for i,v in ipairs(idList) do
local oneId = v:split(":")
returnTable[oneId[1]] = oneId[2]
end
for k,v in pairs(returnTable) do
print (k .. " " .. v)
end
print("size of table: " .. #returnTable)
return returnTable
end
I want to trsnform a string to table.
The function "split" parse a string, split it with parameter as a delimiter, and return as table.
The result of a excution above function like below.
1234 16
9012 1
5678 8
size of table: 0
It shows the content of table exactly as I expected, but its count is not.
Anybody to help me?
Thanks in advance.
The # operator tells you the highest numeric index in the table. If there are any gaps in the numeric indexing, it may return the highest below the gap. Basically, the # operator only works right if you're treating your table like a dense array.
If you actually want to know how many entries are in a table, you'll need to iterate over it using the pairs() function and count how many items you get.
function countTableSize(table)
local n = 0
for k, v in pairs(table) do
n = n + 1
end
return n
end
Although I do wonder why you even need to know how many entries are in the table. Typically all you care about is if the table is empty or not, and you can check that by just seeing if next(table) == nil.