I can't seem to find a solution for how to type out the syntax. I want to access and be able to modify individual integers within an array that's nested in a larger dictionary.
Here's an example:
var exampleDictionary = [ 1: [2,3] , 2: [4,5] , 3: [7,8] ]
print (exampleDictionary[2]!) // prints [4,5]
How can I access the first value of Key 2 (4 above)?
How can I change just the first value of Key 2?
I'm trying things like this and it's not working:
exampleDictionary[2[0]] = (exampleDictionary[2[0]] - 3) // want to change [4,5] to [1,5]
print (exampleDictionary[2[0]]) // should print "1"
exampleDictionary[2 : [0]] = (exampleDictionary[2 :[0]] - 3)
print (exampleDictionary[2 : [0]])
You should subscript the array not its index :
exampleDictionary[2]![0] = (exampleDictionary[2]?[0] ?? 0) - 3
Safer would be to use optional binding like so:
if var array = exampleDictionary[2] {
array[0] -= 3
exampleDictionary[2] = array
}
You have always to consider that Swift collection types are value types and you have to modify the object in place, something like
if let firstValueOfKey2 = exampleDictionary[2]?.first {
print(firstValueOfKey2)
exampleDictionary[2]!.first = firstValueOfKey2 - 3
print(exampleDictionary[2]!.first!)
}
First of all you have dictionary of type [Int: [Int]], which means every key have a value of array from Int.
1.If your exampleDictionary is of unrelated type, Specify the type of exampleDictionary to [Int: [Int]] so that you won't need to cast it in next step.
var exampleDictionary: [Int: [Int]] = [ 1: [2,3] , 2: [4,5] , 3: [7,8] ]
2.Access the key you want.
var key2: [Int] = exampleDictionary[2]!
var firstValueOfKey2: Int = key2.first!
3.Change the value for key2:
key2 = [1,5]
4.Because of Swift, Collections are value type which means that a pointer refers to new block in memory, this means that you cant change the value from root object directly from modifiers. then you should assign the last result to root object.
exampleDictionary[2] = key2
i want from tableview to collect MyArray's as value like
Swift:
let total = UILabel()
var MyArray = ["2", "9", "33", "4"]
total.text = ?? // i want result be like this [2+9+33+4] = 48
and if add some value or remove some the result change
i hope i delivered right question and i hope i get the right answer
Iterate through your array, using conditional binding, if the value is invalid, e.g "hello", it won't enter the condition.
var result = 0
for element in MyArray { // MyArray should have the first letter lowercased and have a more meaningful name.
if let number = Int(element) { // or NSNumber, Double, etc...
result = result + number
}
}
total.text = "\(result)" // consider naming it totalLabel
Convert the myArray elements type from String to Double using compactMap. Then add the elements using reduce method. Then convert the result to string to show in label.
var myArray = ["2", "9", "33", "4", "wot?", "🐶"]
total.text = String(myArray.lazy.compactMap{ Double($0) }.reduce(0, +))//48.0
Two suggestions:
With reduce to sum up the values and ignore non-integer values
total.text = String(myArray.reduce(0, {$0 + (Int($1) ?? 0)}))
With NSExpression if the array contains only string representations of integers. joined converts the array to "2+9+33+4"
let additionExpression = NSExpression(format: myArray.joined(separator: "+"))
total.text = "\(additionExpression.expressionValue(with: nil, context: nil)!)"
There are two steps::
Calculate the total.
Consider:
let array = ["200", "900", "33", "4"]
let total = array
.lazy
.compactMap { Double($0) }
.reduce(0, +)
Note, unlike other suggestions, I’m refraining from placing this in a single line of code (although one could). The goal of functional programming patterns is to write expressive yet efficient code about which it is easy to reason. Placing all of this onto one line is contrary to that goal, IMHO, though it is arguably a matter of personal preference.
Setting the text of the label.
When setting the text of the label, it’s very tempting to want to just do String(total). But that is not a very user-friendly presentation (e.g. the sum 1,137 will be shown as “1137.0”). Nor is it localized.
The typical solution when displaying a result (whether numbers, dates, time intervals, etc.) in the user interface is to use a “formatter”. In the case of numeric values, one would typically use a NumberFormatter:
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
label.text = formatter.string(for: total)
For a user in the US, that will show “1,137”, whereas the German user will see “1.137”. So each device sees the number presented in a format consistent with the users’ localization preferences.
I have the following two hashes with arrays as values.
a = {
"Us" => [["1", ["1", "2"]], ["2", ["1"]]],
"Pa" => [["1", ["1", "3", "5"]], ["4", ["7"]]]
}
b = {
"Us" => [["1", ["F", "O"]], ["2", ["N"]]],
"Pa" => [["1", ["S", "D", "H"]], ["4", ["K"]]]
}
I'm trying to merge the hashes to get a final has like this:
c = {
"Us" => [["1", ["1|F", "2|O"]], ["2", ["1|N"]]],
"Pa" => [["1", ["1|S", "3|D", "5|H"]], ["4", ["7|K"]]]
}
I found the following code with merge, and tried to apply it to my issue, but I got an error:
a.merge(b) {|key, a_val, b_val| a_val.merge b_val }
# >> NoMethodError: undefined method `merge' for [["1", ["1", "2"]], ["2", ["1"]]]:Array
I even got an error with a + b:
a + b
# >> NoMethodError: undefined method `+' for #<Hash:0x0000060078e460>
<<<< UPDATE >>>>
Thanks both Cary and tadman. Outside the original question I show the input file I have and the output I´m tryng to obtain. I show in order for you get an idea why
I generated 2 hashes in that way. In the output I create blocks where fathers are unique values of column 1, below children (unique values in column 2 related with col 1).
Column 3 are subchildren that belong to a value in col2 and column 4 are text contents related with col3.
Probably hash "c" is easier to generate from the beginning.
This is my input file
Main,Stage1,Stage2,Description
Us,1,1,F
Us,1,2,O
Us,2,1,N
Pa,1,1,S
Pa,1,3,D
Pa,1,5,H
Pa,4,7,K
This is the output I almost get.
Main..Stage1..Stage2..Description
Us
......1
..............1.......F
..............2.......O
......2
..............1.......N
Pa
......1
..............1.......S
..............3.......D
..............5.......H
......4
..............7.......K
Then I was able to create this code, but like tadman says, I need to reorder the way I get this to make easier the things, since
I use 4 hashes. After I create hash "a" and "b" I was stuck, since I needed a unique hash to iterate and be able to print in the output structure shown above.
My code before post the question
X = Hash.new{|hsh,key| hsh[key] = [] }
Y = Hash.new{|hsh,key| hsh[key] = [] }
a = Hash.new{|hsh,key| hsh[key] = [] }
b = Hash.new{|hsh,key| hsh[key] = [] }
File.foreach('file.txt').with_index do
|line, line_num|
if line_num > 0
r = line.split(",")
X[r[0] + "°" + r[1]].push r[2]
Y[r[0] + "°" + r[1]].push r[3].strip
end
end
X.each{ |k,v|
lbs = k.split("°")
a[lbs[0]].push [ lbs[1], v] #Here I generate hash a
}
Y.each{ |k,v|
lbs = k.split("°")
b[lbs[0]].push [ lbs[1], v] #Here I generate hash b
}
What you have here is going to require a bit of work to solve because of all the complicated nesting. This would be a lot easier if you did some work to reorder how that data is stored.
Yet you can do this:
a={"Us"=>[["1", ["1", "2"]], ["2", ["1"]]], "Pa"=>[["1", ["1", "3", "5"]], ["4", ["7"]]]}
b={"Us"=>[["1", ["F", "O"]], ["2", ["N"]]], "Pa"=>[["1", ["S", "D", "H"]], ["4", ["K"]]]}
c = a.keys.map do |k|
ah = a[k].to_h
bh = b[k].to_h
[
k,
ah.keys.map do |ka|
[
ka,
ah[ka].zip(bh[ka]).map do |pair|
pair.join('|')
end
]
end
]
end.to_h
# => {"Us"=>[["1", ["1|F", "2|O"]], ["2", ["1|N"]]], "Pa"=>[["1", ["1|S", "3|D", "5|H"]], ["4", ["7|K"]]]}
The key here is rigorous use of map to transform each layer and zip to "zipper" two arrays together into pairs that can then be combined with join into the desired string target. Cast back to a Hash with to_h at the end and you get what you want.
There's an intermediate conversion for each subset to a hash to handle out-of-order situations where one might specify the apparent "keys" in a different sequence.
What you'll want to do is wrap this up in a method with a descriptive name:
def hash_compactor(a,b)
# ... (code) ...
end
That'll help keep it modular. Normally I try and create solutions that handle N arguments by defining it as:
def hash_compactor(*input)
# ...
end
Where input is then an array of various sets in the form you've given. The resulting code is surprisingly a lot more complicated.
Note this makes a lot of assumptions about the input being perfectly matched and will explode if that's not the case.
I suggest you first convert the values of one of the hashes to hashes, as I will explain. Suppose we create a new b.
newbie = b.transform_values(&:to_h)
#=> {"Us"=>{"1"=>["F", "O"], "2"=>["N"]},
# "Pa"=>{"1"=>["S", "D", "H"], "4"=>["K"]}}
We can now use a and newbie to produce the desired return value.
a.each_with_object({}) do |(k,v),h|
h[k] = v.map do |first, arr|
[first, arr.zip(newbie[k][first]).map { |pair| pair.join('|') }]
end
end
#=> {"Us"=>[["1", ["1|F", "2|O"]], ["2", ["1|N"]]],
# "Pa"=>[["1", ["1|S", "3|D", "5|H"]], ["4", ["7|K"]]]}
If a can be mutated it's slightly easier.
a.each do |k,v|
v.map! do |first, arr|
[first, arr.zip(newbie[k][first]).map { |pair| pair.join('|') }]
end
end
The method Hash#trasform_values made its debut in Ruby v2.4. To support older versions, one compute newbie as follows.
newbie = b.each_with_object({}) {|(k,v),h| h[k] = v.to_h }
In this solution we'll keep the original structure.
I've followed your first try but instead of:
a.merge(b) {|key, a_val, b_val| a_val.merge b_val }
Think about use a new custom merge function like:
c = a.merge(b) {|key, a_val, b_val| myMergeArray(a_val, b_val) }
Then the new merge function is a simple recursive one:
def myMergeArray(a,b,sep = '|')
c = a
c.each_with_index { |e, i|
if c[i].is_a? Array
c[i] = myMergeArray(c[i], b[i], sep)
else
c[i] = c[i] + sep + b[i] if c[i] != b[i]
end
}
return c
end
I've assumed that in case of equal elements, just save one, so e.g. "Y" and "Y" yield just "Y" instead of "Y|Y"
Cheers!
I'm using Ruby 2.4. I have an array of strings, which are themselves numbers. So something like
["1", "2", "3", "5"]
How do I check that the integer version of every element in the array (except the first) is greater than the one before it? So for instance the function performed on the above would return true, but an array like
["1", "5", "4", "6"]
would return false (because "4" is not greater than "5".
An alternative way to phrase your predicate is: "For all consecutive pairs of numbers, is it true that the second is greater than the first"? This can be almost directly expressed in code:
ary.map(&:to_i).each_cons(2).all? {|first, second| second > first }
By the way: this property is called "strict monotonicity".
You can use Enumerable#sort_by to see if the arr is already sorted by numerical value:
arr = ["1", "2", "3", "5"]
arr.uniq.sort_by(&:to_i) == arr
#=> true
If the elements are not unique, then the array fails automatically. Since that means two elements are of the same value i.e. one is not greater than the other.
An instance variable in my code keeps on having its value reassigned despite no commands for it to do so. Essentially, the variable is only called in twice: once, to assign its value at startup and then to copy its value to another variable. The code I am working on is a bit complex for me to post it here completely, but this is a basic rundown of it:
class Test
def self.initialize
#problem_var = [ ["1 2 3", "4 5 6"], ["a b c", "d e f"], ["bar", "foo"] ]
end
def self.main_method(parVar)
data = #problem_var
result = "Lorem"
#Iterate through subarrays
data.each do |dataBlock|
#Some code here
if condition then
#The first subarray meets the condition
char = dataBlock[1]
#At this point char is equal to "4 5 6"
##problem_var still holds its original value of:
# [ ["1 2 3", "4 5 6"], ["a b c", "d e f"], ["bar", "foo"] ]
result = OtherModule.replace_six(char)
#By this point, char is now equal to "4 5 7"
#Curiously #problem_var is now equal to:
# [ ["1 2 3, "4 5 7"], ["a b c", "d e f"], ["bar", "foo"] ]
end
end
#More code here
return result
end
end
Something weird happens to the variable right after result has a value assigned to it. Furthermore, this only seems to happen once so that if the code were to run again and alter that 7 to say... an 8, #problem_var would not be updated. Changing #problem_var to a constant has not been able to prevent it from being altered. I have mulled over this for the past two weeks and haven't been able to figure it out. Anyone know what could be happening?
Edit:
You guys were right! The issue was in OtherModule. I was using gsub! on the parameter variable that received char. Here's the simplified OtherModule code for future reference:
module OtherModule
def replace_six(input)
modified_string = ""
if condition(input) then
#Input meets condition
first_string = replace_numbers(input)
#The following method doesn't really apply here
second_string = replace_letters(first_string)
modified_string = second_string
end
return modified_string
end
def replace_numbers(text)
#Some code here
#The following condition for numbers in `text`
if condition(text) then
text.gsub!("6", numberFunction)
#numberFunction returns a string
end
return text
end
end
Problem is most likely in OtherModule.replace_six.
If it uses String#replace method, then, String is mutated and its effect will be visible everywhere a reference to its has been kept.
If you do not have access to code of OtherModule, then, do this:
result = OtherModule.replace_six(char.dup)
If you have access to code of OtherModule, then, change the implementation of replace_six such that it uses either String#sub or String#gsub, as they return a copy of modified string instead of mutating the original string.