iOS swift 4 sort dictionary by a particular values - ios

Can you please help me sorting the dictionary below with value "Val1".
var data = ["key1" : ["val1": 1,"val2": 1],
"key2" : ["val1": 5,"val2": 2],
"key3" : ["val1": 0,"val2": 9]]
I am expecting this dictionary to be sorted like this. Highest values of 'Val1' to be on top (descending order)..
var data = [ "key2" : ["val1": 5,"val2": 2],
"key1" : ["val1": 1,"val2": 1],
"key3" : ["val1": 0,"val2": 9]]

As #Sulthan correctly pointed out Dictionary cannot be sorted. If you still want to, you'll get an Array as result.
let data = [
"key1": ["val1": 1,"val2": 1],
"key2": ["val1": 5,"val2": 2],
"key3": ["val1": 0,"val2": 9]
]
let result = data.sorted { $0.1["val1"]! > $1.1["val1"]! }
print(result)
[(key: "key2", value: ["val1": 5, "val2": 2]),
(key: "key1", value: ["val1": 1, "val2": 1]),
(key: "key3", value: ["val1": 0, "val2": 9])]

Related

Merge sub-array in Swift

I have an array and I want to merge it.
This is the array:
let numbers = [ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
I need a output like this:
let result = [ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ]
let numbers = [[1,2,3], [4,5,6]]
let result = zip(numbers[0], numbers[1]).map { [$0.0, $0.1]}
print(result) // -> [[1, 4], [2, 5], [3, 6]]
If the array has more elements the following would work.
let numbers = [[1,2,3], [4,5,6], [7,8,9]]
var result : [[Int]] = []
for n in 0...numbers.first!.count-1{
result.append(numbers.compactMap { $0[n] })
}
print(result) // -> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
You can use the zip global function, which, given 2 sequences, returns a sequence of tuples and then obtain an array of tuples by using the proper init.
var xAxis = [1, 2, 3, 4]
var yAxis = [2, 3, 4, 5]
let pointsSequence = zip(xAxis, yAxis)
let chartPoints = Array(pointsSequence)
print(chartPoints)
And then you can access the tuples like this :
let point = chartPoints[0]
point.0 // This is the 1st element of the tuple
point.1 // This is the 2nd element of the tuple

Grouping Elements in Dictionary by The Last Character of The Keys [iOS Swift 5]

I have a dictionary that I want to group by the last character of the keys. This is the dictionary:
var displayValues = ["volume_1": 1, "price_2": 6, "price_1": 2, "stock_1": 3, "volume_2": 5, "stock_2": 7]
This is the code that I used in order to group them
let groupValues = Dictionary(grouping: displayValues) { $0.key.last! }
print(groupValues)
This is the result of this code
["2": [(key: "price_2", value: 6), (key: "volume_2", value: 5), (key: "stock_2", value: 7)], "1": [(key: "volume_1", value: 1), (key: "price_1", value: 2), (key: "stock_1", value: 3)]]
The grouping is correct, however, how do I remove the words key and value from the dictionary so that it will display the following?
[
"2": ["price_2": 6, "volume_2" : 5, "stock_2": 7],
"1": ["volume_1": 1, "price_1": 2, "stock_1": 3]
]
You are almost there !!
now You have key as you wanted and value as array of tuple
You can convert array of tuple into dictionary with new reduce(into:)
full code would be
var displayValues = ["volume_1": 1, "price_2": 6, "price_1": 2, "stock_1": 3, "volume_2": 5, "stock_2": 7];
let dict = Dictionary(grouping: displayValues) { $0.key.suffix(1)}
let final = dict. mapValues { value in
return value.reduce(into: [:]) { $0[$1.key] = $1.value }
}
print(final)
Output :
["2": ["price_2": 6, "volume_2": 5, "stock_2": 7], "1": ["price_1": 2, "stock_1": 3, "volume_1": 1]]
In this case, Dictionary(grouping:by:) creates a Dictionary of type [Character : [(key: String, value: Int)]]. So the values are an array of (key: String, value: Int) tuples.
Use .mapValues() to convert the Array of (key: String, value: Int) tuples into a Dictionary by calling Dictionary(uniqueKeysWithValues) with the array:
var displayValues = ["volume_1": 1, "price_2": 6, "price_1": 2, "stock_1": 3, "volume_2": 5, "stock_2": 7]
let groupValues = Dictionary(grouping: displayValues) { String($0.key.suffix(1)) }
.mapValues { Dictionary(uniqueKeysWithValues: $0) }
print(groupValues)
Result:
["1": ["stock_1": 3, "price_1": 2, "volume_1": 1], "2": ["volume_2": 5, "stock_2": 7, "price_2": 6]]
Note:
To avoid a force unwrap (which will crash if you have an empty String as a key), I used String($0.key.suffix(1)) instead of $0.key.last!. This will make the final dictionary [String : [String : Int]] which can be conveniently indexed with a String.
Thanks to #LeoDabus for this suggestion.

How to count consecutive numbers in an array?

If I have an array:
array = [1,2,2,2,2,5,5,1,1,1,3,3,3,3,2,2,2,2,2,2,2]
I want to be able to identify consecutive matching numbers that have a length of greater than 3. And map the starting index of the consecutive numbers. An example output for the above array would be:
consecutive_numbers = [
{starting_index: 1, value: 2, length: 4},
{starting_index: 10, value: 3, length: 4},
{starting_index: 14, value: 2, length: 7}
]
The values can be the same, but the consecutive series' must be mutually exclusive. See that there are 2 hashes with a value of 2, but their starting indexes are different.
My attempt so far... looks like this:
array.each_cons(3).with_index.select{|(a,b,c), i|
[a,b,c].uniq.length == 1
}
but that will returns:
[[[2, 2, 2], 1], [[2, 2, 2], 2], [[1, 1, 1], 7], [[3, 3, 3], 10], [[3, 3, 3], 11], [[2, 2, 2], 14], [[2, 2, 2], 15], [[2, 2, 2], 16], [[2, 2, 2], 17], [[2, 2, 2], 18]]
But that returns overlapping results.
array.each_with_index.
chunk(&:first).
select { |_,a| a.size > 3 }.
map { |n,a| { starting_index: a.first.last, value: n, length: a.size } }
#=> [{:starting_index=> 1, :value=>2, :length=>4},
# {:starting_index=>10, :value=>3, :length=>4},
# {:starting_index=>14, :value=>2, :length=>7}]
The steps are as follows.
e = array.each_with_index.chunk(&:first)
#=> #<Enumerator: #<Enumerator::Generator:0x00005b1944253c18>:each>
We can convert this enumerator to an array to view the elements it will generate and pass to its block.
e.to_a
#=> [[1, [[1, 0]]],
# [2, [[2, 1], [2, 2], [2, 3], [2, 4]]],
# [5, [[5, 5], [5, 6]]],
# [1, [[1, 7], [1, 8], [1, 9]]],
# [3, [[3, 10], [3, 11], [3, 12], [3, 13]]],
# [2, [[2, 14], [2, 15], [2, 16], [2, 17], [2, 18], [2, 19], [2, 20]]]]
Continuing,
c = e.select { |_,a| a.size > 3 }
#=> [[2, [[2, 1], [2, 2], [2, 3], [2, 4]]],
# [3, [[3, 10], [3, 11], [3, 12], [3, 13]]],
# [2, [[2, 14], [2, 15], [2, 16], [2, 17], [2, 18], [2, 19], [2, 20]]]]
c.map { |n,a| { starting_index: a.first.last, value: n, length: a.size } }
#=> [{:starting_index=> 1, :value=>2, :length=>4},
# {:starting_index=>10, :value=>3, :length=>4},
# {:starting_index=>14, :value=>2, :length=>7}]
This is another way.
array.each_with_index.with_object([]) do |(n,i),arr|
if arr.any? && arr.last[:value] == n
arr.last[:length] += 1
else
arr << { starting_index: i, value: n, length: 1 }
end
end.select { |h| h[:length] > 3 }
#=> [{:starting_index=> 1, :value=>2, :length=>4},
# {:starting_index=>10, :value=>3, :length=>4},
# {:starting_index=>14, :value=>2, :length=>7}]
You can chunk_while each pair of elements are equal:
p array.chunk_while { |a, b| a == b }.to_a
# [[1], [2, 2, 2, 2], [5, 5], [1, 1, 1], [3, 3, 3, 3], [2, 2, 2, 2, 2, 2, 2]]
You select the arrays with 3 or more elements.
After that, with then, you can yield self, so you have access to the array of arrays, which you can use to get the starting_index:
[1,2,2,2,2,5,5,1,1,1,3,3,3,3,2,2,2,2,2,2,2].chunk_while(&:==).then do |this|
this.each_with_object([]).with_index do |(e, memo), index|
memo << { starting_index: this.to_a[0...index].flatten.size, value: e.first, length: e.size }
end
end.select { |e| e[:length] > 3 }
# [{:starting_index=>1, :value=>2, :length=>4},
# {:starting_index=>10, :value=>3, :length=>4},
# {:starting_index=>14, :value=>2, :length=>7}]
For the starting_index, you get the elements to the current index (non inclusive), flatten them, and get the total of elements.
The value, as each array in the array has the same elements, can be anything, the length, is the length of the current array in the "main" array.
This is another option..
Zipping the array (Enumerable#zip) with its indexes by Endles Ranges
Calling Enumerable#slice_when (another flavour of chunk_while)
Mapping (Enumerable#map) to the required Hash
Finally rejecting (Enumerable#reject) hashes if length is greater than 3
array
.zip(0..)
.slice_when { |a, b| a.first != b.first }
.map { |a| { starting_index: a.first.last, value: a.first.first, length: a.size } }
.reject { |h| h[:length] < 3 }
#=> [{:starting_index=>1, :value=>2, :length=>4}, {:starting_index=>7, :value=>1, :length=>3}, {:starting_index=>10, :value=>3, :length=>4}, {:starting_index=>14, :value=>2, :length=>7}]
Well, the most obvious (and probably the fastest) way is iterate over an array and count everything manually:
array = [1,2,2,2,2,5,5,1,1,1,3,3,3,3,2,2,2,2,2,2,2]
array_length_pred = array.length.pred
consecutive_numbers = []
starting_index = 0
value = array.first
length = 1
array.each_with_index do |v, i|
if v != value || i == array_length_pred
length += 1 if i == array_length_pred && value == v
if length >= 3
consecutive_numbers << {
starting_index: starting_index,
value: value,
length: length
}
end
starting_index = i
value = v
length = 1
next
end
length += 1
end
p consecutive_numbers
# [{:starting_index=>1, :value=>2, :length=>4},
# {:starting_index=>7, :value=>1, :length=>3},
# {:starting_index=>10, :value=>3, :length=>4},
# {:starting_index=>14, :value=>2, :length=>7}]
You could work with strings instead.
Here, I coerce the array into a string:
input_sequence = [1,2,2,2,2,5,5,1,1,1,3,3,3,3,2,2,2,2,2,2,2].join
I use a regex to group consecutive characters:
groups = input_sequence.gsub(/(.)\1*/).to_a
#=> ["1", "2222", "55", "111", "3333", "2222222"]
Now I can search for the groups as substrings within the input string:
groups.map do |group|
{
starting_index: input_sequence.index(group),
value: group[0].to_i,
length: group.length
}
end.reject { |group| group[:length] <= 3 }
#=> [{:starting_index=>1, :value=>2, :length=>4},
{:starting_index=>7, :value=>1, :length=>3},
{:starting_index=>10, :value=>3, :length=>4},
{:starting_index=>14, :value=>2, :length=>7}]
There's room for improvement here -- I'm creating lots of intermediate objects for one -- but I thought I would offer a different approach.

Using array method map to aggregate an array of dictionaries with an array

I have an array representing (for example) the days of the week:
["Monday", "Tuesday", "Wed", "Thursday", "Friday", "Sat", "Sunday"]
I have a array of dictionaries of the SAME size of my first array, each Dictionary contains only one key/value:
[["temp": 11], ["temp": 12], ["temp": 13], ["temp": 14], ["temp": 15], ["temp": 16], ["temp": 17]]
Each temperature correspond to the day of the same index in the first array.
Is there a way, using the array method map(transform: (Self.Generator.Element) throws -> T), to transform my array of dictionaries into the following dictionary:
["Monday": 11, "Tuesday": 12, "Wed": 13, "Thursday": 14, "Friday": 15, "Sat": 16, "Sunday": 17]
Well, you can return only Array from map and not Dictionary. Here is an alternate way of doing this:
var test1 = [["temp": 11], ["temp": 12], ["temp": 13], ["temp": 14], ["temp": 15], ["temp": 16], ["temp": 17]]
var myArray = ["Monday", "Tuesday", "Wed", "Thursday", "Friday", "Sat", "Sunday"]
var finalArray = [Dictionary<String,Int?>]()
for day in myArray {
let itemIndex = myArray.indexOf(day)
let itemDict = test1[itemIndex!]
let value = itemDict["temp"] as Int?
let changedDict = [day : value]
finalArray.append(changedDict)
}
This is obviously work for zip:
var test1: [[String: Any]] = [["temp": 11], ["temp": 12], ["temp": 13], ["temp": 14], ["temp": 15], ["temp": 16], ["temp": 17]]
var myArray = ["Monday", "Tuesday", "Wed", "Thursday", "Friday", "Sat", "Sunday"]
let temps = test1.compactMap { $0["temp"] as? Int }
let finalArray: [[String: Int]] = zip(temps, myArray)
.map { (temp, day) in [day: temp] }
print(finalArray)

Add and remove data from JSON file [duplicate]

This question already has answers here:
How do I parse JSON with Objective-C?
(5 answers)
Closed 8 years ago.
I added to the project the file Objects.json which contains the following code:
{
"circle": [
[1, 3], [2, 1], [3, 2],
],
"line": [
[2, 1], [3, 2], [1, 3],
],
"A_Letter": [
[3, 3], [2, 1], [1, 2],
],
}
How could I add to this file a new key?
If, for instance, I want to remove the "line" key, how could this be done?
Try this may be helpfull, In this I solve JSON and remove key as you wants ..
NSString *str = #"{\"circle\": [ [1, 3], [2, 1], [3, 2],],\"line\": [ [2, 1], [3, 2], [1, 3],],\"A_Letter\": [ [3, 3], [2, 1], [1, 2], ]}";
NSMutableDictionary *datadic = [NSJSONSerialization JSONObjectWithData:[str dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:nil];
[datadic removeObjectForKey:#"line"];
NSLog(#"data return %#",datadic);
Thanks
Since this is the arrays of dictionary. So for adding into the dictionary use setObject: ForKey: method and for removing use removeObjectForKey: like that below:-
NSMutableDictionary *mutDict=[yourjsonDict mutableCopy];
[mutDict setObject: #[#[1,2],#[2,3]] forKey:#"yourKey"];
// now for removing
[mutDict removeObjectForKey:#"line"];

Resources