Ruby: How to sum values of multiple JSON objects into one object? - ruby-on-rails

I have two JSON objects like this:
object1 = {
"Oct 2019": 100,
"Nov 2019": 100,
"Dec 2019": 100,
"Jan 2020": 100,
"Feb 2020": 100,
"Mar 2020": 100,
"Apr 2020": 100,
"May 2020": 100,
"Jun 2020": 100,
"Jul 2020": 100,
"Aug 2020": 100,
"Sep 2020": 100
}
object2 = {
"Oct 2019": 50,
"Nov 2019": 50,
"Dec 2019": 50,
"Jan 2020": 50,
"Feb 2020": 50,
"Mar 2020": 50,
"Apr 2020": 50,
"May 2020": 50,
"Jun 2020": 50,
"Jul 2020": 50,
"Aug 2020": 50,
"Sep 2020": 50
}
How can I sum the values of object1 and object2 so the resulting json object looks like the following?:
result = {
"Oct 2019": 150,
"Nov 2019": 150,
"Dec 2019": 150,
"Jan 2020": 150,
"Feb 2020": 150,
"Mar 2020": 150,
"Apr 2020": 150,
"May 2020": 150,
"Jun 2020": 150,
"Jul 2020": 150,
"Aug 2020": 150,
"Sep 2020": 150
}
(The values in this example are the same for every month, but in my application they are all different values.)

There are many ways to do that, but I find the easiest is to use the form of Hash#merge that employs a block to compute the values of keys that appear in both hashes being merged.
object1.merge(object2) { |_k,v1,v2| v1+v2 }
# = {:"Oct 2019"=>150, :"Nov 2019"=>150, :"Dec 2019"=>150,
# :"Jan 2020"=>150, :"Feb 2020"=>150, :"Mar 2020"=>150,
# :"Apr 2020"=>150, :"May 2020"=>150, :"Jun 2020"=>150,
# :"Jul 2020"=>150, :"Aug 2020"=>150, :"Sep 2020"=>150}
The three block variables, _k, v1 and v2, are defined in the doc. As is common practice, I've begun the name of the common key (_k) with an underscore to signal to the reader that it is not used in the block calculation.
If there were three or more such hashes to combine, write:
object1.merge(object2, object3, ...) { |_k,v1,v2| v1+v2 }
Another way of doing this is quite simple:
keys = (object1.keys + object2.keys).uniq
#=> [:"Oct 2019", :"Nov 2019",..., :"Sep 2020"]
keys.each_with_object({}) do |k,h|
h[k] = object1.fetch(k,0) + object2.fetch(k,0)
end
#=> {:"Oct 2019"=>150, :"Nov 2019"=>150,..., :"Sep 2020"=>150}
See Hash#fetch.
One could replace objectx.fetch(k,0) with objectx[k].to_i, as nil.to_i #=> 0, but I don't think that reads as well.
This could be extended to summing over three or more hashes in the obvious way.

Related

Merging & Summing nested hashes in Ruby

What I'm trying to do is very similar to the question outlined in this post, but I have one additional problem in that the nested values of my hash need to have their dates grouped and the values of each date summed. The goal is to create a Multiple Series Graph in Chartkick.
The query, grabbing a month range for example:
arr = LineItem.includes(:order, :product)
.where(orders: {order_date: Date.parse("Jan 1 2020")..Date.parse("Feb 1 2020")})
.map { |line_item| { name: line_item.product.model_number, data: { line_item.order.order_date.strftime('%a %b %d, %Y') => line_item.order_quantity } } }
The output hash:
=> [
{:name=>"FR-GP02", :data=>{"Mon Jan 20, 2020"=>2}},
{:name=>"FR-GP02", :data=>{"Mon Jan 20, 2020"=>5}},
{:name=>"FR-GP02", :data=>{"Tue Jan 21, 2020"=>1}},
{:name=>"FR-GP02", :data=>{"Tue Jan 21, 2020"=>3}},
{:name=>"FR-GP02", :data=>{"Wed Jan 22, 2020"=>1}},
{:name=>"FR-GP04", :data=>{"Mon Jan 20, 2020"=>2}},
{:name=>"FR-GP04", :data=>{"Tue Jan 21, 2020"=>4}},
{:name=>"FR-GP04", :data=>{"Tue Jan 21, 2020"=>3}},
{:name=>"FR-GP04", :data=>{"Tue Jan 21, 2020"=>6}},
{:name=>"FR-GP04", :data=>{"Wed Jan 22, 2020"=>3}},
{:name=>"FR-GP01", :data=>{"Tue Jan 21, 2020"=>5}},
{:name=>"FR-GP01", :data=>{"Thu Jan 23, 2020"=>3}},
{:name=>"FR-GP01", :data=>{"Thu Jan 23, 2020"=>1}},
...
My expected hash; which should group the name, then group the date and sum the value:
=> [
{:name=>"FR-GP02", :data=>{"Mon Jan 20, 2020"=>7, "Tue Jan 21, 2020"=>4, "Wed Jan 22, 2020"=>1}},
{:name=>"FR-GP04", :data=>{"Mon Jan 20, 2020"=>2, "Tue Jan 21, 2020"=>13, "Wed Jan 22, 2020"=>3}},
{:name=>"FR-GP01", :data=>{"Tue Jan 21, 2020"=>5, "Thu Jan 23, 2020"=>4}},
...
However, after running this code:
arr.group_by {|h| h[:name]}.map { |k,v| { name: k, data: v.map {|h| h[:data]}.reduce(&:merge)}}
this is the output:
=> [
{:name=>"RP-AP02", :data=>{"Mon Jan 20, 2020"=>2, "Tue Jan 21, 2020"=>1, "Wed Jan 22, 2020"=>1}},
{:name=>"RP-AP04", :data=>{"Mon Jan 20, 2020"=>2, "Tue Jan 21, 2020"=>4, "Wed Jan 22, 2020"=>3}},
{:name=>"RP-AP01", :data=>{"Tue Jan 21, 2020"=>5, "Thu Jan 23, 2020"=>3}},
...
The output generated does group the name and data, but does not sum the quantities. I'm grouping it by day here as an example, but would also like the option of grouping it by week & month. In the past 8 hours of monkeying with this, I've also tried using Groupdate to no avail.
There are many ways to obtain the desired return value. Here are two. First I define arr.
arr = [
{:name=>"FR-GP02", :data=>{"Mon Jan 20, 2020"=>2}},
{:name=>"FR-GP02", :data=>{"Mon Jan 20, 2020"=>5}},
{:name=>"FR-GP02", :data=>{"Tue Jan 21, 2020"=>1}},
{:name=>"FR-GP02", :data=>{"Tue Jan 21, 2020"=>3}},
{:name=>"FR-GP02", :data=>{"Wed Jan 22, 2020"=>1}},
{:name=>"FR-GP04", :data=>{"Mon Jan 20, 2020"=>2}},
{:name=>"FR-GP04", :data=>{"Tue Jan 21, 2020"=>4}},
{:name=>"FR-GP04", :data=>{"Tue Jan 21, 2020"=>3}},
{:name=>"FR-GP04", :data=>{"Tue Jan 21, 2020"=>6}},
{:name=>"FR-GP04", :data=>{"Wed Jan 22, 2020"=>3}},
{:name=>"FR-GP01", :data=>{"Tue Jan 21, 2020"=>5}},
{:name=>"FR-GP01", :data=>{"Thu Jan 23, 2020"=>3}},
{:name=>"FR-GP01", :data=>{"Thu Jan 23, 2020"=>1}}]
The first calculation employs the methods Enumerable#group_by and Hash#transform_values.
arr.group_by { |h| h[:name] }
.map do |k,v|
{ name: k,
data: v.group_by do |h|
h[:data].keys.first
end.transform_values { |a| a.sum { |h| h[:data].values.first }}
}
end
#=> [{:name=>"FR-GP02", :data=>{"Mon Jan 20, 2020"=>7,
"Tue Jan 21, 2020"=>4,
"Wed Jan 22, 2020"=>1}},
{:name=>"FR-GP04", :data=>{"Mon Jan 20, 2020"=>2,
"Tue Jan 21, 2020"=>13,
"Wed Jan 22, 2020"=>3}},
{:name=>"FR-GP01", :data=>{"Tue Jan 21, 2020"=>5,
"Thu Jan 23, 2020"=>4}}]
Note:
arr.group_by { |h| h[:name] }
#=> {"FR-GP02"=>[{:name=>"FR-GP02", :data=>{"Mon Jan 20, 2020"=>2}},
{:name=>"FR-GP02", :data=>{"Mon Jan 20, 2020"=>5}},
{:name=>"FR-GP02", :data=>{"Tue Jan 21, 2020"=>1}},
{:name=>"FR-GP02", :data=>{"Tue Jan 21, 2020"=>3}},
{:name=>"FR-GP02", :data=>{"Wed Jan 22, 2020"=>1}}],
"FR-GP04"=>[{:name=>"FR-GP04", :data=>{"Mon Jan 20, 2020"=>2}},
{:name=>"FR-GP04", :data=>{"Tue Jan 21, 2020"=>4}},
{:name=>"FR-GP04", :data=>{"Tue Jan 21, 2020"=>3}},
{:name=>"FR-GP04", :data=>{"Tue Jan 21, 2020"=>6}},
{:name=>"FR-GP04", :data=>{"Wed Jan 22, 2020"=>3}}],
"FR-GP01"=>[{:name=>"FR-GP01", :data=>{"Tue Jan 21, 2020"=>5}},
{:name=>"FR-GP01", :data=>{"Thu Jan 23, 2020"=>3}},
{:name=>"FR-GP01", :data=>{"Thu Jan 23, 2020"=>1}}]}
map's block variables initially equal the following:
k = "FR-GP02"
v = [{:name=>"FR-GP02", :data=>{"Mon Jan 20, 2020"=>2}},
{:name=>"FR-GP02", :data=>{"Mon Jan 20, 2020"=>5}},
{:name=>"FR-GP02", :data=>{"Tue Jan 21, 2020"=>1}},
{:name=>"FR-GP02", :data=>{"Tue Jan 21, 2020"=>3}},
{:name=>"FR-GP02", :data=>{"Wed Jan 22, 2020"=>1}}]
Then the value of :data in the first hash being created is computed as follows:
f = v.group_by do |h|
h[:data].keys.first
end
#=> {"Mon Jan 20, 2020"=>[
# {:name=>"FR-GP02", :data=>{"Mon Jan 20, 2020"=>2}},
# {:name=>"FR-GP02", :data=>{"Mon Jan 20, 2020"=>5}}],
# "Tue Jan 21, 2020"=>[
# {:name=>"FR-GP02", :data=>{"Tue Jan 21, 2020"=>1}},
# {:name=>"FR-GP02", :data=>{"Tue Jan 21, 2020"=>3}}],
# "Wed Jan 22, 2020"=>[
# {:name=>"FR-GP02", :data=>{"Wed Jan 22, 2020"=>1}}]}
and lastly,
f.transform_values { |a| a.sum { |h| h[:data].values.first }}
#=> {"Mon Jan 20, 2020"=>7, "Tue Jan 21, 2020"=>4, "Wed Jan 22, 2020"=>1}
Here is a second way to obtain the desired result.
arr.each_with_object(Hash.new(0)) do |g,h|
d, n = g[:data].flatten
h[[g[:name], d]] += n
end.group_by { |(name, _),_| name }
.map do |name,arr|
{ name: name, data: arr.each_with_object({}) { |((_,d),t),h| h[d] = t } }
end
#=> (as above)
The steps are as follows.
s = arr.each_with_object(Hash.new(0)) do |g,h|
d, n = g[:data].flatten
h[[g[:name], d]] += n
end
#=> {["FR-GP02", "Mon Jan 20, 2020"]=>7,
# ["FR-GP02", "Tue Jan 21, 2020"]=>4,
# ["FR-GP02", "Wed Jan 22, 2020"]=>1,
# ["FR-GP04", "Mon Jan 20, 2020"]=>2,
# ["FR-GP04", "Tue Jan 21, 2020"]=>13,
# ["FR-GP04", "Wed Jan 22, 2020"]=>3,
# ["FR-GP01", "Tue Jan 21, 2020"]=>5,
# ["FR-GP01", "Thu Jan 23, 2020"]=>4}
This uses the form of Hash::new that takes an argument called its default value (usually, as here, zero) and no block. If a hash is defined
h = Hash.new(0)
and--possibly after adding key-value pairs--does not have a key k, h[k] will return the default value. This means that in the expression
h[[g[:name], d]] += n
if h does not have a key [g[:name], d] the value of h for that key is initialized to zero before n is added. If h does have that key the current value of that key is increased by n.
Continuing the calculation,
t = s.group_by { |(name,_),_| name }
#=> {"FR-GP02"=>[[["FR-GP02", "Mon Jan 20, 2020"], 7],
# [["FR-GP02", "Tue Jan 21, 2020"], 4],
# [["FR-GP02", "Wed Jan 22, 2020"], 1]],
# "FR-GP04"=>[[["FR-GP04", "Mon Jan 20, 2020"], 2],
# [["FR-GP04", "Tue Jan 21, 2020"], 13],
# [["FR-GP04", "Wed Jan 22, 2020"], 3]],
# "FR-GP01"=>[[["FR-GP01", "Tue Jan 21, 2020"], 5],
# [["FR-GP01", "Thu Jan 23, 2020"], 4]]}
Lastly,
t.map do |name,arr|
{ name: name, data: arr.each_with_object({}) { |((_,d),t),h| h[d] = t } }
end
#=> (as above)
Here and earlier I've made good use of Ruby's powerful technique called Array decomposition. See also this article.

Why isn't the offset of Samoa +13 or +14 when using pytz?

I've just read
BBC: Samoa and Tokelau skip a day for dateline change, 30.12.2011
I wanted to see this with pytz, but everything I tried only showed an offset of -11, but not of +13 or +14:
>>> import pytz
>>> tz = pytz.timezone('Pacific/Samoa')
>>> tz_us = pytz.timezone('US/Samoa')
>>> import datetime
>>> datetime.datetime(2011, 12, 30, 9, 00, tzinfo=datetime.timezone.utc).astimezone(tz).isoformat()
'2011-12-29T22:00:00-11:00'
>>> datetime.datetime(2011, 12, 30, 10,00, tzinfo=datetime.timezone.utc).astimezone(tz).isoformat()
'2011-12-29T23:00:00-11:00'
>>> datetime.datetime(2011, 12, 30, 11, 00, tzinfo=datetime.timezone.utc).astimezone(tz).isoformat()
'2011-12-30T00:00:00-11:00'
>>> datetime.datetime(2011, 12, 31, 15, 00, tzinfo=datetime.timezone.utc).astimezone(tz).isoformat()
'2011-12-31T04:00:00-11:00'
>>> datetime.datetime(2015, 12, 31, 15, 00, tzinfo=datetime.timezone.utc).astimezone(tz).isoformat()
'2015-12-31T04:00:00-11:00'
>>> datetime.datetime(2011, 12, 31, 15, 00, tzinfo=datetime.timezone.utc).astimezone(tz_us).isoformat()
'2011-12-31T04:00:00-11:00'
>>> datetime.datetime(2015, 12, 31, 15, 00, tzinfo=datetime.timezone.utc).astimezone(tz_us).isoformat()
'2015-12-31T04:00:00-11:00'
Why can't I see the offset +13 / +14?
Both Pacific/Samoa and US/Samoa are aliases of Pacific/Pago_Pago, representing American Samoa, which is UTC-11 and did not skip that day.
For American Samoa, use Pacific/Pago_Pago
For the Independent State of Samoa, use Pacific/Apia
For Tokelau, use Pacific/Fakaofo
Personally, I prefer to only use canonical zone names. See the list on Wikipedia for reference.
See the timezone change with pytz
UTC time with offset:
>>> import pytz
>>> tz = pytz.timezone('Pacific/Apia')
>>> import datetime
>>> datetime.datetime(2011, 12, 30, 9, 59, tzinfo=datetime.timezone.utc).astimezone(tz).isoformat()
'2011-12-29T23:59:00-10:00'
>>> datetime.datetime(2011, 12, 30, 10, 00, tzinfo=datetime.timezone.utc).astimezone(tz).isoformat()
'2011-12-31T00:00:00+14:00'
Local time:
>>> '{:%Y-%m-%d %H:%M}'.format(datetime.datetime(2011, 12, 30, 9, 59, tzinfo=datetime.timezone.utc).astimezone(tz))
'2011-12-29 23:59'
>>> '{:%Y-%m-%d %H:%M}'.format(datetime.datetime(2011, 12, 30, 10, 00, tzinfo=datetime.timezone.utc).astimezone(tz))
'2011-12-31 00:00'

How to draw 2 LineCharts with xAxis with Strings

I am new in programming and swift and I am trying to make a chart with 2 lines. And on top of the chart to enumerate the Array of Strings that I have (DD-MM in my case). I managed to draw just one line like so:
let testArrayTemp = [22.43, 14.86,20.63, 17.08,23.68,14.12,11.09,13.89, 15.0, 9.86, 7.71,10.0,11.94, 8.68, 8.91,6.81, 9.03,8.89, 9.7, 9.26, 9.43,10.22, 9.04,8.04, 7.56,10.44, 7.22, 13.67,9.44, 7.67, 5.99]
let testArrayFeel = [20.43, 13.86,10.63, 15.08,22.68,11.12,10.09,15.89, 13.0, 6.86, 5.71,11.0,10.94, 7.68, 6.91,5.81, 3.03,5.89, 7.7, 8.26, 8.43, 11.22, 6.04,7.04, 6.56,11.44, 6.22, 12.67,10.44, 5.67, 4.99]
let testDayArray = ["Oct 2, 2016", "Oct 3, 2016", "Oct 4, 2016", "Oct 5, 2016", "Oct 6, 2016", "Oct 7, 2016", "Oct 8, 2016", "Oct 9, 2016", "Oct 10, 2016", "Oct 11, 2016", "Oct 12, 2016", "Oct 13, 2016", "Oct 14, 2016", "Oct 15, 2016", "Oct 16, 2016", "Oct 17, 2016", "Oct 18, 2016", "Oct 19, 2016", "Oct 20, 2016", "Oct 21, 2016", "Oct 22, 2016", "Oct 23, 2016", "Oct 24, 2016", "Oct 25, 2016", "Oct 26, 2016", "Oct 27, 2016", "Oct 28, 2016", "Oct 29, 2016", "Oct 30, 2016", "Oct 31, 2016", "Nov 1, 2016"]
setChart(dataPoints: testDayArray, valuesTempChart: testArrayTemp, valuesFeelChart: testArrayFeel)
}
//MARK:- Set Chart
func setChart(dataPoints: [String], valuesTempChart: [Double], valuesFeelChart: [Double])
{
var tempEntries: [ChartDataEntry] = []
for i in 0..<dataPoints.count
{
let dataEntry = ChartDataEntry(x: Double(i), y: valuesTempChart[i])
tempEntries.append(dataEntry)
}
let lineChartDataSetTemp = LineChartDataSet(values: tempEntries, label: "Temperature")
lineChartDataSetTemp.setColor(UIColor.red)
// lineChartDataSetTemp.mode = .cubicBezier
lineChartDataSetTemp.drawCirclesEnabled = true
lineChartDataSetTemp.lineWidth = 2.0
lineChartDataSetTemp.circleRadius = 5.0
lineChartDataSetTemp.highlightColor = UIColor.green
lineChartDataSetTemp.drawHorizontalHighlightIndicatorEnabled = true
var dataSets = [IChartDataSet]()
dataSets.append(lineChartDataSetTemp)
let lineChartDataTemp = LineChartData(dataSets: dataSets)
lineChart.data = lineChartDataTemp
lineChart.animate(xAxisDuration: 2.0, yAxisDuration: 2.0)
lineChart.noDataText = "There is no provided data from the server. Please check out later!"
}
but it looks like so:
what I need to do to show the String array on top (with the days) and to add one more line. please any idea, or maybe a link to a tutorial in SWIFT 3 not other version, cause they made a lot of changes, and nothing from swift 2 tutorials works as it should, but the autocorrection does even worse.
I would like it to look something like this: (but with 2 lines and days in stead of months)
Thank you for any suggestions
What you need to do is repeat the process for the feels data. Reference the code below.
func setChart(dataPoints: [String], valuesTempChart: [Double],valuesFeelChart: [Double])
{
var tempEntries: [ChartDataEntry] = []
var feelEntries: [ChartDataEntry] = []
for i in 0..<dataPoints.count
{
let dataEntryOne = ChartDataEntry(x: Double(i), y: valuesTempChart[i])
let dataEntryTwo = ChartDataEntry(x: Double(i), y: valuesFeelChart[i])
tempEntries.append(dataEntryOne)
feelEntries.append(dataEntryTwo)
}
let lineChartDataSetTemp = LineChartDataSet(values: tempEntries, label: "Temperature")
let lineChartDataSetFeels = LineChartDataSet(values: feelEntries, label: "Feels")
var dataSets = [IChartDataSet]()
dataSets.append(lineChartDataSetTemp)
dataSets.append(lineChartDataSetFeels)
let lineChartD = LineChartData(dataSets: dataSets)
lineChart.data = lineChartD
lineChart.animate(xAxisDuration: 2.0, yAxisDuration: 2.0)
lineChart.noDataText = "There is no provided data from the server. Please check out later!"
}
Hope this helps!

Cut elements of array in Swift 2

I have array of elements (it's a basic array). Type of array is String
basicArray = [1710, 1725, 1740, 1755, 1810, 1825, 1840, 1855, 1925, 1955, 2020, 2050, 2120, 2150, 2220, 2250, 2320, 2350, 2430]
I need to create two new arrays where each element of basicArray must be cut into two parts, for example:
array1 = [17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 20, 20....]
array2 = [10, 25, 40, 55, 10, 25, 40, 55, 25,55, 20, 50...]
How better to do it? Thank you for your advice!
let basic = ["1710", "1725", "1740", "1755", "1810", "1825", "1840", "1855", "1925", "1955", "2020", "2050", "2120", "2150", "2220", "2250", "2320", "2350", "2430"]
let array1 = basic.map { String($0.characters.prefix(2)) }
let array2 = basic.map { String($0.characters.suffix(2)) }
print(array1)
print(array2)
Output:
["17", "17", "17", "17", "18", "18", "18", "18", "19", "19", "20", "20", "21", "21", "22", "22", "23", "23", "24"]
["10", "25", "40", "55", "10", "25", "40", "55", "25", "55", "20", "50", "20", "50", "20", "50", "20", "50", "30"]
Something like this?
Try this (Hints are in the code comments):
var basicArray = [1710, 1725, 1740, 1755, 1810, 1825, 1840, 1855, 1925, 1955, 2020, 2050, 2120, 2150, 2220, 2250, 2320, 2350, 2430]
var firstTwoDigitsArray = [Int]()
var lastTwoDigitsArray = [Int]()
for element in basicArray {
// dividing by 100 shifts the numbers down by the first two digits
firstTwoDigitsArray.append(element/100)
// modulo 100 gets the last two digits
lastTwoDigitsArray.append(element%100)
}
print(firstTwoDigitsArray) // [17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24]
print(lastTwoDigitsArray) // [10, 25, 40, 55, 10, 25, 40, 55, 25, 55, 20, 50, 20, 50, 20, 50, 20, 50, 30]
Swift has a built in method called filter which basically is a closure that iterates over all elements and lets you -- as the name suggests -- filter the elements based on a predicate:
let array = [1,2,3,4,5,6]
let array1 = array.filter { $0 % 2 == 0 }
print(array1) // [2, 4, 6]
let array2 = array.filter { $0 % 2 == 1 }
print(array2) // [1,3,5]
You haven't made clear how you want those arrays to be filtered, so it's hard to explicitly give the answer that you might be looking for...

Make NSDictionary Monthwise from DateArray

I have an array of DateString , in which I have values of date,month and year .
Now I have sorted it in ascending order . Now I have to put it in to dictionary with this key-value pair :-
Key- "January" (Month Name) and Value:- array of dates in that month
can anyone suggest me how to do this ?
You write code, as everyone would do.
You need a dictionary that maps (year, month) to (array).
You start with an empty mutable dictionary. Then you loop through your items. For each item, get the year and month (I assume you don't want Januarys of different years together). Look up the year / month in the dictionary, getting the mutable array value. If it isn't there, add the (year, month) key with a new mutable array as the value. So now you have an array for the (year, month), and add the item to that array.
If your data is fixed at that point, you can iterate through the dictionary and replace all the mutable arrays with immutable arrays (just use copy). And instead of sorting all the items first, just sort the keys of the dictionary, so you are now sorted by month and year, then sort the arrays when you need them sorted. For example in a tableview, you only need those arrays sorted that are actually being displayed.
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [[NSDateComponents alloc] init];
// Set your year and month here
[components setYear:2016];
NSMutableDictionary *dayAndMonthWiseList = [[NSMutableDictionary alloc] init];
for (int i=1; i<=12; i++)
{
[components setMonth:i];
NSDate *date = [calendar dateFromComponents:components];
NSRange range = [calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:date];
NSMutableArray *dayList = [[[NSMutableArray alloc] init] mutableCopy];
for (int j=1; j<=range.length; j++)
{
[dayList addObject:[NSString stringWithFormat:#"%d",j]];
}
[dayAndMonthWiseList setObject:dayList forKey:[NSString stringWithFormat:#"%d",i]];
}
NSLog(#"Dic. :%#",dayAndMonthWiseList);
Result is :
Dic. :{
1 = (
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,
26,
27,
28,
29,
30,
31
);
10 = (
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31
);
11 = (
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30
);
12 = (
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31
);
2 = (
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29
);
3 = (
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31
);
4 = (
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30
);
5 = (
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31
);
6 = (
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30
);
7 = (
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31
);
8 = (
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31
);
9 = (
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30
);
}

Resources