I recently started learning JavaScript and faced a task that I can't complete in any way, every time I get the wrong data that I need
There is an object that contains data on banking transactions, I need to make a selection and form a new object using filter, map or reduce:
We assume that the initial balance on the card = 0.
Output the TOP 3 months with the largest number of operations by month.
Formalize it as a task_1(arr) function, where arr is the source array with data
for all months.
Output format:
[
{ year: 2019, month: 11, opsCount: 27 },
{ year: 2019, month: 10, opsCount: 26 },
{ year: 2019, month: 8, opsCount: 24 }
]
Output statistics for the end of the specified month.
monthmonthBalance - own balance by month = The amount of all
deposits minus the amount of all debits
montWithdrawal – total cash withdrawal amount
withdrawalRate – the share of the total amount of debits from the total amount of
deposits per month.
rank – user status calculated by the formula:
Gold if withdrawalRate < 0.15.
Silver if withdrawalRate < 0.3.
Bronze in all other cases.
Formalize it as a task_2(year, month, arr) function, where year, month is the desired one
month, and arr is the original array with all the data by month.
Output format:
{
date: '2019-01-31’,
monthBalance: 3829,
monthWithrawal: 33800,
withdrawalRate: 0.3133
rank: 'Bronze’
}
Calculate statistics from task 2 for all months of the period.
Add a field to each month
totalBalance - cumulative balance = Own balance for the
month + Balance for the cumulative balance for the previous month.
Output format:
[
{
date: '2019-01-31’,
monthBalance: 3829,
totalBalance: 3829,
monthWithrawal: 33800,
withdrawalRate: 0.3133
rank: ’Bronze'
},
...
]
Formalize it as a task_3(arr) function, where arr is the source array with data.
JSON with data:
[
{ "year": 2019, "month": 1, "day": 1, "type": "replenishment", "amount": 79817 },
{ "year": 2019, "month": 1, "day": 3, "type": "payment", "amount": 11334 },
{ "year": 2019, "month": 1, "day": 5, "type": "withdrawal", "amount": 26700 },
{ "year": 2019, "month": 1, "day": 5, "type": "payment", "amount": 15475 },
{ "year": 2019, "month": 1, "day": 6, "type": "payment", "amount": 4818 },
{ "year": 2019, "month": 1, "day": 6, "type": "payment", "amount": 1893 },
{ "year": 2019, "month": 1, "day": 7, "type": "payment", "amount": 3844 },
{ "year": 2019, "month": 1, "day": 7, "type": "withdrawal", "amount": 3100 },
{ "year": 2019, "month": 1, "day": 7, "type": "payment", "amount": 3230 },
{ "year": 2019, "month": 1, "day": 7, "type": "payment", "amount": 2427 },
{ "year": 2019, "month": 1, "day": 9, "type": "replenishment", "amount": 15835 },
{ "year": 2019, "month": 1, "day": 10, "type": "payment", "amount": 9670 },
{ "year": 2019, "month": 1, "day": 11, "type": "payment", "amount": 582 },
{ "year": 2019, "month": 1, "day": 11, "type": "withdrawal", "amount": 1100 },
{ "year": 2019, "month": 1, "day": 11, "type": "replenishment", "amount": 5971 },
{ "year": 2019, "month": 1, "day": 12, "type": "payment", "amount": 173 },
{ "year": 2019, "month": 1, "day": 14, "type": "withdrawal", "amount": 1500 },
{ "year": 2019, "month": 1, "day": 14, "type": "payment", "amount": 3641 },
{ "year": 2019, "month": 1, "day": 16, "type": "payment", "amount": 4669 },
{ "year": 2019, "month": 1, "day": 18, "type": "payment", "amount": 2460 },
{ "year": 2019, "month": 1, "day": 19, "type": "payment", "amount": 1307 },
{ "year": 2019, "month": 1, "day": 20, "type": "withdrawal", "amount": 1400 },
{ "year": 2019, "month": 1, "day": 21, "type": "payment", "amount": 952 },
{ "year": 2019, "month": 1, "day": 21, "type": "payment", "amount": 561 },
{ "year": 2019, "month": 1, "day": 21, "type": "replenishment", "amount": 6236 },
{ "year": 2019, "month": 1, "day": 22, "type": "payment", "amount": 888 },
{ "year": 2019, "month": 1, "day": 22, "type": "payment", "amount": 2306 },
{ "year": 2019, "month": 2, "day": 1, "type": "replenishment", "amount": 84839 },
{ "year": 2019, "month": 2, "day": 1, "type": "withdrawal", "amount": 27700 },
{ "year": 2019, "month": 2, "day": 1, "type": "payment", "amount": 11145 },
{ "year": 2019, "month": 2, "day": 2, "type": "payment", "amount": 4075 },
{ "year": 2019, "month": 2, "day": 4, "type": "withdrawal", "amount": 10900 },
{ "year": 2019, "month": 2, "day": 6, "type": "payment", "amount": 10537 },
{ "year": 2019, "month": 2, "day": 6, "type": "payment", "amount": 6001 },
{ "year": 2019, "month": 2, "day": 7, "type": "withdrawal", "amount": 7300 },
{ "year": 2019, "month": 2, "day": 10, "type": "withdrawal", "amount": 1500 },
{ "year": 2019, "month": 2, "day": 10, "type": "payment", "amount": 3584 },
{ "year": 2019, "month": 2, "day": 11, "type": "payment", "amount": 701 },
{ "year": 2019, "month": 3, "day": 1, "type": "replenishment", "amount": 84771 },
{ "year": 2019, "month": 3, "day": 3, "type": "withdrawal", "amount": 22700 },
{ "year": 2019, "month": 3, "day": 5, "type": "payment", "amount": 12352 },
{ "year": 2019, "month": 3, "day": 8, "type": "payment", "amount": 2795 },
{ "year": 2019, "month": 3, "day": 11, "type": "withdrawal", "amount": 16600 },
{ "year": 2019, "month": 3, "day": 11, "type": "replenishment", "amount": 4141 },
{ "year": 2019, "month": 3, "day": 11, "type": "payment", "amount": 9854 },
{ "year": 2019, "month": 3, "day": 13, "type": "withdrawal", "amount": 1200 },
{ "year": 2019, "month": 3, "day": 14, "type": "payment", "amount": 11573 },
{ "year": 2019, "month": 3, "day": 14, "type": "payment", "amount": 5138 },
{ "year": 2019, "month": 3, "day": 15, "type": "payment", "amount": 731 },
{ "year": 2019, "month": 3, "day": 17, "type": "payment", "amount": 5053 },
{ "year": 2019, "month": 3, "day": 19, "type": "withdrawal", "amount": 400 },
{ "year": 2019, "month": 3, "day": 20, "type": "payment", "amount": 1745 },
{ "year": 2019, "month": 3, "day": 20, "type": "payment", "amount": 602 },
{ "year": 2019, "month": 3, "day": 21, "type": "payment", "amount": 178 },
{ "year": 2019, "month": 3, "day": 22, "type": "payment", "amount": 353 },
{ "year": 2019, "month": 3, "day": 23, "type": "payment", "amount": 837 },
{ "year": 2019, "month": 3, "day": 23, "type": "payment", "amount": 330 },
{ "year": 2019, "month": 3, "day": 23, "type": "payment", "amount": 799 },
{ "year": 2019, "month": 3, "day": 24, "type": "payment", "amount": 294 },
{ "year": 2019, "month": 3, "day": 24, "type": "payment", "amount": 260 },
{ "year": 2019, "month": 3, "day": 25, "type": "withdrawal", "amount": 200 },
{ "year": 2019, "month": 4, "day": 1, "type": "replenishment", "amount": 88656 },
{ "year": 2019, "month": 4, "day": 4, "type": "payment", "amount": 37852 },
{ "year": 2019, "month": 4, "day": 7, "type": "payment", "amount": 9365 },
{ "year": 2019, "month": 4, "day": 8, "type": "payment", "amount": 16701 },
{ "year": 2019, "month": 4, "day": 11, "type": "payment", "amount": 8979 },
{ "year": 2019, "month": 4, "day": 11, "type": "payment", "amount": 1971 },
{ "year": 2019, "month": 4, "day": 13, "type": "payment", "amount": 1261 },
{ "year": 2019, "month": 4, "day": 13, "type": "withdrawal", "amount": 800 },
{ "year": 2019, "month": 4, "day": 15, "type": "payment", "amount": 5553 },
{ "year": 2019, "month": 4, "day": 17, "type": "payment", "amount": 2593 },
{ "year": 2019, "month": 4, "day": 18, "type": "replenishment", "amount": 6915 },
{ "year": 2019, "month": 4, "day": 19, "type": "replenishment", "amount": 17647 },
{ "year": 2019, "month": 4, "day": 21, "type": "payment", "amount": 13814 },
{ "year": 2019, "month": 4, "day": 22, "type": "payment", "amount": 2707 },
{ "year": 2019, "month": 4, "day": 22, "type": "withdrawal", "amount": 1300 },
{ "year": 2019, "month": 4, "day": 22, "type": "withdrawal", "amount": 2900 },
{ "year": 2019, "month": 4, "day": 23, "type": "replenishment", "amount": 10709 },
{ "year": 2019, "month": 4, "day": 23, "type": "payment", "amount": 686 },
{ "year": 2019, "month": 4, "day": 23, "type": "withdrawal", "amount": 5100 },
{ "year": 2019, "month": 4, "day": 23, "type": "payment", "amount": 3830 },
{ "year": 2019, "month": 5, "day": 1, "type": "replenishment", "amount": 59877 },
{ "year": 2019, "month": 5, "day": 2, "type": "payment", "amount": 14095 },
{ "year": 2019, "month": 5, "day": 5, "type": "payment", "amount": 10858 },
{ "year": 2019, "month": 5, "day": 8, "type": "payment", "amount": 9412 },
{ "year": 2019, "month": 5, "day": 11, "type": "replenishment", "amount": 6892 },
{ "year": 2019, "month": 5, "day": 11, "type": "payment", "amount": 17541 },
{ "year": 2019, "month": 5, "day": 11, "type": "payment", "amount": 2666 },
{ "year": 2019, "month": 5, "day": 11, "type": "payment", "amount": 3935 },
{ "year": 2019, "month": 5, "day": 12, "type": "withdrawal", "amount": 2600 },
{ "year": 2019, "month": 5, "day": 14, "type": "payment", "amount": 2096 },
{ "year": 2019, "month": 5, "day": 14, "type": "replenishment", "amount": 2733 },
{ "year": 2019, "month": 5, "day": 15, "type": "replenishment", "amount": 538 },
{ "year": 2019, "month": 5, "day": 15, "type": "payment", "amount": 5324 },
{ "year": 2019, "month": 5, "day": 15, "type": "payment", "amount": 2490 },
{ "year": 2019, "month": 5, "day": 15, "type": "payment", "amount": 3510 },
{ "year": 2019, "month": 5, "day": 17, "type": "withdrawal", "amount": 300 },
{ "year": 2019, "month": 5, "day": 17, "type": "payment", "amount": 133 },
{ "year": 2019, "month": 6, "day": 1, "type": "replenishment", "amount": 89064 },
{ "year": 2019, "month": 6, "day": 2, "type": "payment", "amount": 7613 },
{ "year": 2019, "month": 6, "day": 2, "type": "payment", "amount": 33742 },
{ "year": 2019, "month": 6, "day": 5, "type": "withdrawal", "amount": 7200 },
{ "year": 2019, "month": 6, "day": 6, "type": "payment", "amount": 15125 },
{ "year": 2019, "month": 6, "day": 9, "type": "payment", "amount": 3379 },
{ "year": 2019, "month": 6, "day": 10, "type": "payment", "amount": 1260 },
{ "year": 2019, "month": 6, "day": 12, "type": "payment", "amount": 11066 },
{ "year": 2019, "month": 6, "day": 12, "type": "replenishment", "amount": 7050 },
{ "year": 2019, "month": 6, "day": 12, "type": "payment", "amount": 7531 },
{ "year": 2019, "month": 6, "day": 13, "type": "payment", "amount": 4776 },
{ "year": 2019, "month": 6, "day": 13, "type": "replenishment", "amount": 4456 },
{ "year": 2019, "month": 6, "day": 14, "type": "replenishment", "amount": 7998 },
{ "year": 2019, "month": 6, "day": 16, "type": "payment", "amount": 2437 },
{ "year": 2019, "month": 6, "day": 16, "type": "replenishment", "amount": 11729 },
{ "year": 2019, "month": 6, "day": 18, "type": "payment", "amount": 11216 },
{ "year": 2019, "month": 6, "day": 19, "type": "payment", "amount": 3420 },
{ "year": 2019, "month": 6, "day": 19, "type": "payment", "amount": 1339 },
{ "year": 2019, "month": 6, "day": 20, "type": "payment", "amount": 5578 },
{ "year": 2019, "month": 6, "day": 21, "type": "withdrawal", "amount": 1600 },
{ "year": 2019, "month": 6, "day": 21, "type": "withdrawal", "amount": 400 },
{ "year": 2019, "month": 7, "day": 1, "type": "replenishment", "amount": 51749 },
{ "year": 2019, "month": 7, "day": 2, "type": "payment", "amount": 2875 },
{ "year": 2019, "month": 7, "day": 2, "type": "payment", "amount": 10315 },
{ "year": 2019, "month": 7, "day": 5, "type": "payment", "amount": 18501 },
{ "year": 2019, "month": 7, "day": 5, "type": "payment", "amount": 12728 },
{ "year": 2019, "month": 7, "day": 7, "type": "payment", "amount": 4505 },
{ "year": 2019, "month": 7, "day": 8, "type": "payment", "amount": 2758 },
{ "year": 2019, "month": 7, "day": 8, "type": "payment", "amount": 60 },
{ "year": 2019, "month": 7, "day": 10, "type": "withdrawal", "amount": 1100 },
{ "year": 2019, "month": 7, "day": 12, "type": "withdrawal", "amount": 1000 },
{ "year": 2019, "month": 7, "day": 13, "type": "payment", "amount": 151 },
{ "year": 2019, "month": 8, "day": 1, "type": "replenishment", "amount": 85156 },
{ "year": 2019, "month": 8, "day": 1, "type": "payment", "amount": 33978 },
{ "year": 2019, "month": 8, "day": 2, "type": "payment", "amount": 6548 },
{ "year": 2019, "month": 8, "day": 3, "type": "payment", "amount": 5909 },
{ "year": 2019, "month": 8, "day": 6, "type": "payment", "amount": 2326 },
{ "year": 2019, "month": 8, "day": 6, "type": "payment", "amount": 17798 },
{ "year": 2019, "month": 8, "day": 9, "type": "replenishment", "amount": 10770 },
{ "year": 2019, "month": 8, "day": 10, "type": "withdrawal", "amount": 7400 },
{ "year": 2019, "month": 8, "day": 12, "type": "payment", "amount": 6065 },
{ "year": 2019, "month": 8, "day": 14, "type": "withdrawal", "amount": 900 },
{ "year": 2019, "month": 8, "day": 14, "type": "withdrawal", "amount": 1400 },
{ "year": 2019, "month": 8, "day": 14, "type": "payment", "amount": 4673 },
{ "year": 2019, "month": 8, "day": 15, "type": "payment", "amount": 960 },
{ "year": 2019, "month": 8, "day": 15, "type": "payment", "amount": 1085 },
{ "year": 2019, "month": 8, "day": 17, "type": "payment", "amount": 3723 },
{ "year": 2019, "month": 8, "day": 17, "type": "payment", "amount": 2522 },
{ "year": 2019, "month": 8, "day": 19, "type": "replenishment", "amount": 2496 },
{ "year": 2019, "month": 8, "day": 20, "type": "payment", "amount": 876 },
{ "year": 2019, "month": 8, "day": 20, "type": "payment", "amount": 2504 },
{ "year": 2019, "month": 8, "day": 21, "type": "payment", "amount": 826 },
{ "year": 2019, "month": 8, "day": 22, "type": "payment", "amount": 768 },
{ "year": 2019, "month": 8, "day": 23, "type": "withdrawal", "amount": 700 },
{ "year": 2019, "month": 8, "day": 23, "type": "payment", "amount": 190 },
{ "year": 2019, "month": 8, "day": 24, "type": "payment", "amount": 235 },
{ "year": 2019, "month": 9, "day": 1, "type": "replenishment", "amount": 95512 },
{ "year": 2019, "month": 9, "day": 3, "type": "payment", "amount": 26758 },
{ "year": 2019, "month": 9, "day": 3, "type": "replenishment", "amount": 8377 },
{ "year": 2019, "month": 9, "day": 4, "type": "payment", "amount": 30865 },
{ "year": 2019, "month": 9, "day": 4, "type": "withdrawal", "amount": 12800 },
{ "year": 2019, "month": 9, "day": 7, "type": "payment", "amount": 10518 },
{ "year": 2019, "month": 9, "day": 8, "type": "payment", "amount": 11007 },
{ "year": 2019, "month": 9, "day": 10, "type": "payment", "amount": 5613 },
{ "year": 2019, "month": 9, "day": 10, "type": "withdrawal", "amount": 1700 },
{ "year": 2019, "month": 9, "day": 12, "type": "payment", "amount": 2237 },
{ "year": 2019, "month": 9, "day": 14, "type": "payment", "amount": 885 },
{ "year": 2019, "month": 9, "day": 14, "type": "payment", "amount": 977 },
{ "year": 2019, "month": 9, "day": 15, "type": "payment", "amount": 766 },
{ "year": 2019, "month": 9, "day": 17, "type": "payment", "amount": 360 },
{ "year": 2019, "month": 9, "day": 18, "type": "payment", "amount": 116 },
{ "year": 2019, "month": 9, "day": 18, "type": "withdrawal", "amount": 200 },
{ "year": 2019, "month": 9, "day": 19, "type": "payment", "amount": 115 },
{ "year": 2019, "month": 9, "day": 20, "type": "payment", "amount": 50 },
{ "year": 2019, "month": 9, "day": 21, "type": "payment", "amount": 32 },
{ "year": 2019, "month": 10, "day": 1, "type": "replenishment", "amount": 90475 },
{ "year": 2019, "month": 10, "day": 1, "type": "replenishment", "amount": 8845 },
{ "year": 2019, "month": 10, "day": 2, "type": "payment", "amount": 7121 },
{ "year": 2019, "month": 10, "day": 3, "type": "payment", "amount": 27955 },
{ "year": 2019, "month": 10, "day": 3, "type": "payment", "amount": 23079 },
{ "year": 2019, "month": 10, "day": 4, "type": "payment", "amount": 5948 },
{ "year": 2019, "month": 10, "day": 7, "type": "withdrawal", "amount": 4400 },
{ "year": 2019, "month": 10, "day": 8, "type": "payment", "amount": 9677 },
{ "year": 2019, "month": 10, "day": 9, "type": "payment", "amount": 3912 },
{ "year": 2019, "month": 10, "day": 9, "type": "replenishment", "amount": 3870 },
{ "year": 2019, "month": 10, "day": 9, "type": "payment", "amount": 6949 },
{ "year": 2019, "month": 10, "day": 10, "type": "withdrawal", "amount": 3400 },
{ "year": 2019, "month": 10, "day": 10, "type": "replenishment", "amount": 7471 },
{ "year": 2019, "month": 10, "day": 10, "type": "payment", "amount": 5962 },
{ "year": 2019, "month": 10, "day": 10, "type": "payment", "amount": 4990 },
{ "year": 2019, "month": 10, "day": 10, "type": "withdrawal", "amount": 3000 },
{ "year": 2019, "month": 10, "day": 11, "type": "withdrawal", "amount": 200 },
{ "year": 2019, "month": 10, "day": 12, "type": "withdrawal", "amount": 1300 },
{ "year": 2019, "month": 10, "day": 13, "type": "payment", "amount": 986 },
{ "year": 2019, "month": 10, "day": 14, "type": "replenishment", "amount": 4225 },
{ "year": 2019, "month": 10, "day": 15, "type": "withdrawal", "amount": 900 },
{ "year": 2019, "month": 10, "day": 17, "type": "payment", "amount": 864 },
{ "year": 2019, "month": 10, "day": 17, "type": "withdrawal", "amount": 1000 },
{ "year": 2019, "month": 10, "day": 18, "type": "payment", "amount": 801 },
{ "year": 2019, "month": 10, "day": 19, "type": "withdrawal", "amount": 300 },
{ "year": 2019, "month": 10, "day": 20, "type": "payment", "amount": 530 },
{ "year": 2019, "month": 11, "day": 1, "type": "replenishment", "amount": 80285 },
{ "year": 2019, "month": 11, "day": 3, "type": "payment", "amount": 38155 },
{ "year": 2019, "month": 11, "day": 6, "type": "payment", "amount": 10260 },
{ "year": 2019, "month": 11, "day": 9, "type": "payment", "amount": 11013 },
{ "year": 2019, "month": 11, "day": 10, "type": "payment", "amount": 1232 },
{ "year": 2019, "month": 11, "day": 12, "type": "withdrawal", "amount": 5100 },
{ "year": 2019, "month": 11, "day": 12, "type": "payment", "amount": 1192 },
{ "year": 2019, "month": 11, "day": 13, "type": "withdrawal", "amount": 4500 },
{ "year": 2019, "month": 11, "day": 14, "type": "replenishment", "amount": 4304 },
{ "year": 2019, "month": 11, "day": 15, "type": "withdrawal", "amount": 700 },
{ "year": 2019, "month": 11, "day": 15, "type": "replenishment", "amount": 15857 },
{ "year": 2019, "month": 11, "day": 17, "type": "payment", "amount": 9134 },
{ "year": 2019, "month": 11, "day": 19, "type": "payment", "amount": 8090 },
{ "year": 2019, "month": 11, "day": 20, "type": "payment", "amount": 2117 },
{ "year": 2019, "month": 11, "day": 20, "type": "withdrawal", "amount": 2700 },
{ "year": 2019, "month": 11, "day": 21, "type": "withdrawal", "amount": 2200 },
{ "year": 2019, "month": 11, "day": 21, "type": "payment", "amount": 258 },
{ "year": 2019, "month": 11, "day": 21, "type": "withdrawal", "amount": 1200 },
{ "year": 2019, "month": 11, "day": 21, "type": "payment", "amount": 1966 },
{ "year": 2019, "month": 11, "day": 21, "type": "withdrawal", "amount": 200 },
{ "year": 2019, "month": 11, "day": 21, "type": "payment", "amount": 493 },
{ "year": 2019, "month": 11, "day": 21, "type": "payment", "amount": 396 },
{ "year": 2019, "month": 11, "day": 21, "type": "withdrawal", "amount": 200 },
{ "year": 2019, "month": 11, "day": 21, "type": "payment", "amount": 134 },
{ "year": 2019, "month": 11, "day": 22, "type": "replenishment", "amount": 4815 },
{ "year": 2019, "month": 11, "day": 22, "type": "withdrawal", "amount": 500 },
{ "year": 2019, "month": 11, "day": 23, "type": "payment", "amount": 1793 },
{ "year": 2019, "month": 12, "day": 1, "type": "replenishment", "amount": 93524 },
{ "year": 2019, "month": 12, "day": 2, "type": "payment", "amount": 44289 },
{ "year": 2019, "month": 12, "day": 2, "type": "payment", "amount": 7724 },
{ "year": 2019, "month": 12, "day": 4, "type": "payment", "amount": 9420 },
{ "year": 2019, "month": 12, "day": 4, "type": "withdrawal", "amount": 3200 },
{ "year": 2019, "month": 12, "day": 4, "type": "payment", "amount": 651 },
{ "year": 2019, "month": 12, "day": 6, "type": "payment", "amount": 9259 },
{ "year": 2019, "month": 12, "day": 6, "type": "withdrawal", "amount": 5700 },
{ "year": 2019, "month": 12, "day": 7, "type": "payment", "amount": 1298 },
{ "year": 2019, "month": 12, "day": 9, "type": "payment", "amount": 3108 },
{ "year": 2019, "month": 12, "day": 11, "type": "withdrawal", "amount": 4300 },
{ "year": 2019, "month": 12, "day": 13, "type": "withdrawal", "amount": 200 },
{ "year": 2019, "month": 12, "day": 13, "type": "replenishment", "amount": 9096 },
{ "year": 2019, "month": 12, "day": 14, "type": "payment", "amount": 7205 },
{ "year": 2019, "month": 12, "day": 16, "type": "payment", "amount": 658 },
{ "year": 2019, "month": 12, "day": 17, "type": "replenishment", "amount": 9654 }
]
First, for the task_1, I get an object that contains values by the key "month":
var arr1 = ops.map(function(item){
return item.month
})
Then I find the sum of the unique values in the resulting array:
quantity var = {};
for (var i = 0; i <arr1.length; i++){
quantity [arr1[i]] = 1 + (quantity [arr1[i]]|/ 0);
}
and I get the counts object in the following form:
counts
{1: 27, 2: 11, 3: 23, 4: 20, 5: 17, 6: 21, 7: 11, 8: 24, 9: 19, 10: 26, 11: 27, 12: 16}
After that, I sort to find the 3 maximum values:
function findMax 3(obj){
var res = [-1,-1,-1];
for (let key in obj){
res[3] = obj[key];
res.sort(function(a,b){return b-a});
}
res.pop();
return res;
}
console.log(findMax3(counts));
In the console I get the following:
[27, 27, 26]
but at the same time, I no longer know the index of the months to which these values relate
Thank you for your answers!
To solve these problems here is the hint "GroupBy using reduce"
As for task1
function task_1(arr) {
// Dictionary of Month to Object.
const groupedByMonth = arr.reduce(function (acc, currentValue) {
let groupKey = currentValue.month;
if (!acc[groupKey]) {
acc[groupKey] = {
year: currentValue.year,
month: currentValue.month,
opsCount: 0
};
}
acc[groupKey].opsCount += 1; // ++
return acc;
}, {});
// Sort by opsCount
function opsSort(a, b) { return b.opsCount - a.opsCount };
return Object
.values(groupedByMonth) // Array of Values
.sort(opsSort)
.slice(0, 3) // Top 3
}
Once you understand how task 1 is solved, task 2 becomes a bit simpler
Solution for Task 2
function getEndOfMonth(year, month) {
const date = new Date(year, month, 0);
let monthStr = "";
if (month < 10) {
monthStr += "0";
}
monthStr += month;
return year + "-" + monthStr + "-" + date.getDate();
}
function getRank(rate) {
if (rate < 0.15)
return 'Gold';
if (rate < 0.3)
return 'Gold';
return 'Bronze';
}
function task_2(arr) {
const groupedByMonth = arr.reduce(function (acc, currentValue) {
let groupKey = currentValue.month;
if (!acc[groupKey]) {
acc[groupKey] = {
date: getEndOfMonth(currentValue.year, currentValue.month),
monthWithrawal: 0,
totalDebits: 0,
totalDeposits: 0
};
}
// Based on type calculate value.
if (currentValue.type === "replenishment") {
acc[groupKey].totalDeposits += currentValue.amount;
} else if (currentValue.type === "payment") {
acc[groupKey].totalDebits += currentValue.amount;
} else if (currentValue.type === "withdrawal") {
acc[groupKey].monthWithrawal += currentValue.amount;
}
return acc;
}, {});
return Object
.values(groupedByMonth) // Array of Values
.map(function (ele) {
const withdrawalRate = ele.monthWithrawal / ele.totalDeposits;
return {
date: ele.date,
monthBalance: ele.totalDeposits - ele.totalDebits - ele.monthWithrawal,
monthWithrawal: ele.monthWithrawal,
withdrawalRate,
rank: getRank(withdrawalRate)
};
});
}
if you have solved task 2, task 3 is just adding reduce/map to task2's solution.
function task_3(arr) {
// Assuming that task_2 function is defined.
const task2 = task_2(arr);
// Can be Solved using reduce.
return task2
.map(function (currentValue, index, array) {
const tmp = currentValue;
tmp.totalBalance = tmp.monthBalance;
if (index > 0) { // Not the first element.
tmp.totalBalance += array[index - 1].totalBalance;
}
return tmp;
})
}
Task 1
const task_1 = (arr, top = 3) => {
const ops = arr.reduce((acc, {year, month}) => {
const accByMonth = acc.find((obj) => (obj.month === month));
if (accByMonth) {
accByMonth.opsCount +=1;
} else {
acc.push({ year, month, opsCount: 1 })
}
return acc;
}, []);
const sortedOps = ops.sort(
(obj1, obj2) => (obj2.opsCount - obj1.opsCount)
);
return sortedOps.slice(0, top);
}
console.log(task_1(data));
//[
// { year: 2019, month: 1, opsCount: 27 },
// { year: 2019, month: 11, opsCount: 27 },
// { year: 2019, month: 10, opsCount: 26 }
//]
Task 2
const getTotalByType = (arr, type) => arr
.filter((obj) => obj.type === type)
.reduce((sum, { amount }) => sum + amount, 0);
const getRank = (ratio) =>{
const ranks = [[0.15, 'Gold'], [0.3, 'Silver'], [Infinity, 'Bronze']]
return ranks.find(([ rankRatio ]) => ratio < rankRatio)[1];
}
const task_2 = (year, month, arr) => {
const filteredData = arr.filter((obj) => (obj.year === year) && (obj.month === month));
const maxDay = Math.max(...filteredData.map(({ day }) => day));
const date = new Date(year, month - 1, maxDay).toISOString().split('T')[0];
const monthReplenishment = getTotalByType(filteredData, 'replenishment');
const monthPayment = getTotalByType(filteredData, 'payment');
const monthWithdrawal = getTotalByType(filteredData, 'withdrawal');
const monthBalance = monthReplenishment - monthPayment - monthWithdrawal;
const withdrawalRate = parseFloat((monthWithdrawal / monthReplenishment).toFixed(4));
const rank = getRank(withdrawalRate);
return {
date,
monthBalance,
monthWithdrawal,
withdrawalRate,
rank,
}
};
console.log(task_2(2019, 7, data));
//{
// date: '2019-07-13',
// monthBalance: -2244,
// monthWithdrawal: 2100,
// withdrawalRate: 0.0406,
// rank: 'Gold'
//}
Task 3
const getPeriods = (arr) => arr.
reduce((acc, {year, month}) => {
const isPeriodInAcc = acc.some(
(obj) => (obj.month === month) && (obj.year === year)
);
if (!isPeriodInAcc) {
acc.push({ year, month })
}
return acc;
}, [])
const task_3 = (arr, initialBalance = 0) => {
const periods = getPeriods(arr);
let totalBalance = initialBalance;
return periods.map(({ year, month }) => {
const opsByPeriod = task_2(year, month, arr);
totalBalance += opsByPeriod.monthBalance;
return {...opsByPeriod, totalBalance}
});
}
console.log(task_3(data));
//[
// {
// date: '2019-01-22',
// monthBalance: 3829,
// monthWithdrawal: 33800,
// withdrawalRate: 0.3134,
// rank: 'Bronze',
// totalBalance: 3829
// },
// ...
// {...},
//]
I am trying to expand this force layout example by changing a nodes' shape from circle to rectangle when it is clicked. So I don't want to change any data but just want to replace the corresponding SVG element.
One of my approaches looked like this:
node.on("click", function() {
this.remove();
svg.selectAll(".node").data(graph.nodes).enter().append("rect")
.attr("class", "node")
.attr("width", 5).attr("height", 5)
.style("fill", function(d) { return color(d.group); });
});
So I removed the SVG element from the DOM and rebound the data, adding a rectangle for the now missing node.
Unfortunately this does not work (the force layout does not set any properties on the new element) and I have no idea if this a reasonable approach at all.
Any ideas how to do this properly?
Try this way.
node.on("click", function(d) {
var size = d.weight * 2 + 12;
d3.select(this).select("circle").remove();
d3.select(this).append("rect")
.attr("x", -(size / 2))
.attr("y", -(size / 2))
.attr("height", size)
.attr("width", size)
.style("fill", function(d) {
return color(1 / d.rating);
});
});
Working code snippet -
var graph = {
"nodes": [{
"name": "1",
"rating": 90,
"id": 2951
}, {
"name": "2",
"rating": 80,
"id": 654654
}, {
"name": "3",
"rating": 80,
"id": 6546544
}, {
"name": "4",
"rating": 1,
"id": 68987978
}, {
"name": "5",
"rating": 1,
"id": 9878933
}, {
"name": "6",
"rating": 1,
"id": 6161
}, {
"name": "7",
"rating": 1,
"id": 64654
}, {
"name": "8",
"rating": 20,
"id": 354654
}, {
"name": "9",
"rating": 50,
"id": 8494
}, {
"name": "10",
"rating": 1,
"id": 6846874
}, {
"name": "11",
"rating": 1,
"id": 5487
}, {
"name": "12",
"rating": 80,
"id": "parfum_kenzo"
}, {
"name": "13",
"rating": 1,
"id": 65465465
}, {
"name": "14",
"rating": 90,
"id": "jungle_de_kenzo"
}, {
"name": "15",
"rating": 20,
"id": 313514
}, {
"name": "16",
"rating": 40,
"id": 36543614
}, {
"name": "17",
"rating": 100,
"id": "Yann_YA645"
}, {
"name": "18",
"rating": 1,
"id": 97413
}, {
"name": "19",
"rating": 1,
"id": 97414
}, {
"name": "20",
"rating": 100,
"id": 976431231
}, {
"name": "21",
"rating": 1,
"id": 9416
}, {
"name": "22",
"rating": 1,
"id": 998949
}, {
"name": "23",
"rating": 100,
"id": 984941
}, {
"name": "24",
"rating": 100,
"id": "99843"
}, {
"name": "25",
"rating": 1,
"id": 94915
}, {
"name": "26",
"rating": 1,
"id": 913134
}, {
"name": "27",
"rating": 1,
"id": 9134371
}],
"links": [{
"source": 6,
"target": 5,
"value": 6,
"label": "publishedOn"
}, {
"source": 8,
"target": 5,
"value": 6,
"label": "publishedOn"
}, {
"source": 7,
"target": 1,
"value": 4,
"label": "containsKeyword"
}, {
"source": 8,
"target": 10,
"value": 3,
"label": "containsKeyword"
}, {
"source": 7,
"target": 14,
"value": 4,
"label": "publishedBy"
}, {
"source": 8,
"target": 15,
"value": 6,
"label": "publishedBy"
}, {
"source": 9,
"target": 1,
"value": 6,
"label": "depicts"
}, {
"source": 10,
"target": 1,
"value": 6,
"label": "depicts"
}, {
"source": 16,
"target": 1,
"value": 6,
"label": "manageWebsite"
}, {
"source": 16,
"target": 2,
"value": 5,
"label": "manageWebsite"
}, {
"source": 16,
"target": 3,
"value": 6,
"label": "manageWebsite"
}, {
"source": 16,
"target": 4,
"value": 6,
"label": "manageWebsite"
}, {
"source": 19,
"target": 18,
"value": 2,
"label": "postedOn"
}, {
"source": 18,
"target": 1,
"value": 6,
"label": "childOf"
}, {
"source": 17,
"target": 19,
"value": 8,
"label": "describes"
}, {
"source": 18,
"target": 11,
"value": 6,
"label": "containsKeyword"
}, {
"source": 17,
"target": 13,
"value": 3,
"label": "containsKeyword"
}, {
"source": 20,
"target": 13,
"value": 3,
"label": "containsKeyword"
}, {
"source": 20,
"target": 21,
"value": 3,
"label": "postedOn"
}, {
"source": 22,
"target": 20,
"value": 3,
"label": "postedOn"
}, {
"source": 23,
"target": 21,
"value": 3,
"label": "manageWebsite"
}, {
"source": 23,
"target": 24,
"value": 3,
"label": "manageWebsite"
}, {
"source": 23,
"target": 25,
"value": 3,
"label": "manageWebsite"
}, {
"source": 23,
"target": 26,
"value": 3,
"label": "manageWebsite"
}]
}
var margin = {
top: -5,
right: -5,
bottom: -5,
left: -5
};
var width = 500 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-200)
.linkDistance(50)
.size([width + margin.left + margin.right, height + margin.top + margin.bottom]);
var zoom = d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed);
var drag = d3.behavior.drag()
.origin(function(d) {
return d;
})
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
var svg = d3.select("#map").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.right + ")")
.call(zoom);
var rect = svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all");
var container = svg.append("g");
//d3.json('http://blt909.free.fr/wd/map2.json', function(error, graph) {
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = container.append("g")
.attr("class", "links")
.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) {
return Math.sqrt(d.value);
});
var node = container.append("g")
.attr("class", "nodes")
.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.call(drag);
node.append("circle")
.attr("r", function(d) {
return d.weight * 2 + 12;
})
.style("fill", function(d) {
return color(1 / d.rating);
});
force.on("tick", function() {
link.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
var linkedByIndex = {};
graph.links.forEach(function(d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
function isConnected(a, b) {
return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index];
}
node.on("click", function(d) {
var size = d.weight * 2 + 12;
d3.select(this).select("circle").remove();
d3.select(this).append("rect")
.attr("x", -(size / 2))
.attr("y", -(size / 2))
.attr("height", size)
.attr("width", size)
.style("fill", function(d) {
return color(1 / d.rating);
});
});
function dottype(d) {
d.x = +d.x;
d.y = +d.y;
return d;
}
function zoomed() {
container.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d3.select(this).classed("dragging", true);
force.start();
}
function dragged(d) {
d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
}
function dragended(d) {
d3.select(this).classed("dragging", false);
}
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.node-active {
stroke: #555;
stroke-width: 1.5px;
}
.link {
stroke: #555;
stroke-opacity: .3;
}
.link-active {
stroke-opacity: 1;
}
.overlay {
fill: none;
pointer-events: all;
}
#map {
border: 2px #555 dashed;
width: 500px;
height: 400px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body>
<div id="map"></div>
</body>
You've updated the dom elements, but not the array used by the force layout algorithm, so do this as the last line in the on click function:
graph.nodes = svg.selectAll(".node");
(You might also find without a data key function that random nodes get changed to rectangles rather than the one you clicked)
I'm having trouble with a d3.js bar chart. The full code and working demo are here: https://jsfiddle.net/monotasker/yurqkm3n/. The issue is that when I use the brush on the lower (context) chart, the upper (focus) chart overflows its margins.
Here is the function that the brush activates:
function brushed() {
time.domain(brush.empty() ? navTime.domain() : brush.extent())
.range([0, (width)]);
x.domain(max_extent_in_days(time))
.rangeBands([margin.left, (width)], 0.1, 0);
focus.selectAll('.bar.stack')
.attr('transform', function(d) {return "translate(" + time(d.date) + ",0)"; })
.attr('width', x.rangeBand());
focus.selectAll('.rect')
.attr('width', x.rangeBand());
focus.selectAll('.line').attr('d', line);
focus.select(".x.axis").call(x_axis);
};
One oddity about the chart is that there are two scales that together control the x axis: "time" and "x". The "time" scale is used for positioning the content on the x axis, but the "x" scale (which converts "time" to an ordinal scale) is used for calculating bar widths. I don't know whether this approach is somehow the source of the problem.
Any help is greatly appreciated.
As in this example where Mr. Bostock applies a clip path to the area path, you need to apply one to the bars. The easiest way is to put them in their own g then apply the clip-path to that:
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
...
// Plot the stacked bars
var focus_bar = focus
.append("g") //<-- g just for the bars to be clipped
.attr("class","barClipper")
.style('clip-path', 'url(#clip)') //<-- apply clipping
.selectAll('.g')
.data(data.answer_counts)
.enter().append('g')
.attr('class', 'g bar stack')
.attr('transform', function(d) {
return "translate(" + x(d.date) + ",0)"
});
Full code:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
<style>
#milestones_attempts_combo .context {
/*fill: #efefef; */
}
#milestones_attempts_combo .focus .right,
#milestones_attempts_combo .context .right {
fill: #0064cd;
}
#milestones_attempts_combo .focus .wrong,
#milestones_attempts_combo .context .wrong {
fill: #cd001d;
}
#milestones_attempts_combo .y-axis-label,
#milestones_attempts_combo .y2-axis-label {
fill: #aaa;
font-size: 120%;
}
#milestones_attempts_combo .focus-line,
#milestones_attempts_combo .context-line {
stroke: orange;
stroke-width: 2;
fill-opacity: 0;
}
.brush .extent {
stroke: #efefef;
fill: #666;
fill-opacity: .125;
shape-rendering: crispEdges;
}
.stats-container {
width: 700px;
margin: 0 auto;
}
.chart .axis line,
.chart .axis path,
.chart .tick line {
fill: none;
stroke: #aaa;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<div id="milestones_attempts_combo"></div>
<script>
(function() {
var myjsondata = {
"answer_counts": [{
"date": "2012-09-13",
"ys": [{
"y1": 9,
"y0": 0,
"class": "right"
}, {
"y1": 12,
"y0": 9,
"class": "wrong"
}],
"total": 12
}, {
"date": "2012-09-16",
"ys": [{
"y1": 16,
"y0": 0,
"class": "right"
}, {
"y1": 17,
"y0": 16,
"class": "wrong"
}],
"total": 17
}, {
"date": "2012-09-17",
"ys": [{
"y1": 12,
"y0": 0,
"class": "right"
}, {
"y1": 14,
"y0": 12,
"class": "wrong"
}],
"total": 14
}, {
"date": "2012-09-19",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 2,
"y0": 2,
"class": "wrong"
}],
"total": 2
}, {
"date": "2012-09-20",
"ys": [{
"y1": 12,
"y0": 0,
"class": "right"
}, {
"y1": 16,
"y0": 12,
"class": "wrong"
}],
"total": 16
}, {
"date": "2012-09-21",
"ys": [{
"y1": 20,
"y0": 0,
"class": "right"
}, {
"y1": 22,
"y0": 20,
"class": "wrong"
}],
"total": 22
}, {
"date": "2012-09-22",
"ys": [{
"y1": 1,
"y0": 0,
"class": "right"
}, {
"y1": 1,
"y0": 1,
"class": "wrong"
}],
"total": 1
}, {
"date": "2012-09-23",
"ys": [{
"y1": 10,
"y0": 0,
"class": "right"
}, {
"y1": 12,
"y0": 10,
"class": "wrong"
}],
"total": 12
}, {
"date": "2012-09-24",
"ys": [{
"y1": 9,
"y0": 0,
"class": "right"
}, {
"y1": 9,
"y0": 9,
"class": "wrong"
}],
"total": 9
}, {
"date": "2012-09-25",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 4,
"y0": 2,
"class": "wrong"
}],
"total": 4
}, {
"date": "2012-09-29",
"ys": [{
"y1": 26,
"y0": 0,
"class": "right"
}, {
"y1": 37,
"y0": 26,
"class": "wrong"
}],
"total": 37
}, {
"date": "2012-10-01",
"ys": [{
"y1": 44,
"y0": 0,
"class": "right"
}, {
"y1": 44,
"y0": 44,
"class": "wrong"
}],
"total": 44
}, {
"date": "2012-10-02",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 2,
"y0": 2,
"class": "wrong"
}],
"total": 2
}, {
"date": "2012-10-03",
"ys": [{
"y1": 13,
"y0": 0,
"class": "right"
}, {
"y1": 13,
"y0": 13,
"class": "wrong"
}],
"total": 13
}, {
"date": "2012-10-05",
"ys": [{
"y1": 47,
"y0": 0,
"class": "right"
}, {
"y1": 47,
"y0": 47,
"class": "wrong"
}],
"total": 47
}, {
"date": "2012-10-08",
"ys": [{
"y1": 17,
"y0": 0,
"class": "right"
}, {
"y1": 17,
"y0": 17,
"class": "wrong"
}],
"total": 17
}, {
"date": "2012-10-09",
"ys": [{
"y1": 19,
"y0": 0,
"class": "right"
}, {
"y1": 20,
"y0": 19,
"class": "wrong"
}],
"total": 20
}, {
"date": "2012-10-10",
"ys": [{
"y1": 31,
"y0": 0,
"class": "right"
}, {
"y1": 31,
"y0": 31,
"class": "wrong"
}],
"total": 31
}, {
"date": "2012-10-11",
"ys": [{
"y1": 6,
"y0": 0,
"class": "right"
}, {
"y1": 6,
"y0": 6,
"class": "wrong"
}],
"total": 6
}, {
"date": "2012-10-14",
"ys": [{
"y1": 6,
"y0": 0,
"class": "right"
}, {
"y1": 6,
"y0": 6,
"class": "wrong"
}],
"total": 6
}, {
"date": "2012-10-19",
"ys": [{
"y1": 30,
"y0": 0,
"class": "right"
}, {
"y1": 32,
"y0": 30,
"class": "wrong"
}],
"total": 32
}, {
"date": "2012-10-20",
"ys": [{
"y1": 20,
"y0": 0,
"class": "right"
}, {
"y1": 22,
"y0": 20,
"class": "wrong"
}],
"total": 22
}, {
"date": "2012-10-23",
"ys": [{
"y1": 20,
"y0": 0,
"class": "right"
}, {
"y1": 20,
"y0": 20,
"class": "wrong"
}],
"total": 20
}, {
"date": "2012-10-24",
"ys": [{
"y1": 20,
"y0": 0,
"class": "right"
}, {
"y1": 21,
"y0": 20,
"class": "wrong"
}],
"total": 21
}, {
"date": "2012-10-29",
"ys": [{
"y1": 22,
"y0": 0,
"class": "right"
}, {
"y1": 22,
"y0": 22,
"class": "wrong"
}],
"total": 22
}, {
"date": "2012-11-01",
"ys": [{
"y1": 1,
"y0": 0,
"class": "right"
}, {
"y1": 1,
"y0": 1,
"class": "wrong"
}],
"total": 1
}, {
"date": "2012-11-02",
"ys": [{
"y1": 26,
"y0": 0,
"class": "right"
}, {
"y1": 29,
"y0": 26,
"class": "wrong"
}],
"total": 29
}, {
"date": "2012-11-05",
"ys": [{
"y1": 23,
"y0": 0,
"class": "right"
}, {
"y1": 27,
"y0": 23,
"class": "wrong"
}],
"total": 27
}, {
"date": "2012-11-06",
"ys": [{
"y1": 10,
"y0": 0,
"class": "right"
}, {
"y1": 11,
"y0": 10,
"class": "wrong"
}],
"total": 11
}, {
"date": "2012-11-07",
"ys": [{
"y1": 15,
"y0": 0,
"class": "right"
}, {
"y1": 18,
"y0": 15,
"class": "wrong"
}],
"total": 18
}, {
"date": "2012-11-09",
"ys": [{
"y1": 26,
"y0": 0,
"class": "right"
}, {
"y1": 31,
"y0": 26,
"class": "wrong"
}],
"total": 31
}, {
"date": "2012-11-10",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 2,
"y0": 2,
"class": "wrong"
}],
"total": 2
}, {
"date": "2012-11-14",
"ys": [{
"y1": 15,
"y0": 0,
"class": "right"
}, {
"y1": 17,
"y0": 15,
"class": "wrong"
}],
"total": 17
}, {
"date": "2012-11-16",
"ys": [{
"y1": 20,
"y0": 0,
"class": "right"
}, {
"y1": 23,
"y0": 20,
"class": "wrong"
}],
"total": 23
}, {
"date": "2012-11-18",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 2,
"y0": 2,
"class": "wrong"
}],
"total": 2
}, {
"date": "2012-11-19",
"ys": [{
"y1": 23,
"y0": 0,
"class": "right"
}, {
"y1": 26,
"y0": 23,
"class": "wrong"
}],
"total": 26
}, {
"date": "2012-11-21",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 3,
"y0": 2,
"class": "wrong"
}],
"total": 3
}, {
"date": "2012-11-23",
"ys": [{
"y1": 9,
"y0": 0,
"class": "right"
}, {
"y1": 10,
"y0": 9,
"class": "wrong"
}],
"total": 10
}, {
"date": "2012-11-26",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 2,
"y0": 2,
"class": "wrong"
}],
"total": 2
}, {
"date": "2012-11-27",
"ys": [{
"y1": 25,
"y0": 0,
"class": "right"
}, {
"y1": 26,
"y0": 25,
"class": "wrong"
}],
"total": 26
}, {
"date": "2012-11-28",
"ys": [{
"y1": 73,
"y0": 0,
"class": "right"
}, {
"y1": 75,
"y0": 73,
"class": "wrong"
}],
"total": 75
}, {
"date": "2012-12-02",
"ys": [{
"y1": 19,
"y0": 0,
"class": "right"
}, {
"y1": 20,
"y0": 19,
"class": "wrong"
}],
"total": 20
}, {
"date": "2012-12-05",
"ys": [{
"y1": 1,
"y0": 0,
"class": "right"
}, {
"y1": 1,
"y0": 1,
"class": "wrong"
}],
"total": 1
}, {
"date": "2013-01-11",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 3,
"y0": 2,
"class": "wrong"
}],
"total": 3
}, {
"date": "2013-01-16",
"ys": [{
"y1": 8,
"y0": 0,
"class": "right"
}, {
"y1": 8,
"y0": 8,
"class": "wrong"
}],
"total": 8
}],
"badge_set_reached": [{
"date": "2014-05-24",
"set": 3
}, {
"date": "2014-09-29",
"set": 5
}, {
"date": "2014-11-10",
"set": 6
}, {
"date": "2014-08-29",
"set": 7
}, {
"date": "2015-08-12",
"set": 9
}, {
"date": "2016-01-09",
"set": 9
}]
}
var data = myjsondata;
// preprocess data to get usable date objects
var parse_date = d3.time.format('%Y-%m-%d').parse
for (var i in data) {
var myObj = data[i]
for (var j in myObj) {
myObj[j].date = parse_date(myObj[j].date);
}
}
console.log(data);
// set variables
var margin = {
left: 60,
right: 60,
top: 10,
bottom: 140
},
navMargin = {
top: 300,
right: 60,
bottom: 40,
left: 60
},
height = 400 - margin.top - margin.bottom,
width = 800 - margin.left - margin.right,
navWidth = width, // for context band
navHeight = 400 - navMargin.top - navMargin.bottom,
max_total_counts = d3.max(data.answer_counts, function(d) {
return d.total;
}),
max_extent_dates = d3.extent(data.answer_counts, function(d) {
return d.date;
});
max_extent_in_days = function(timescale) {
return d3.time.days(timescale.domain()[0], d3.time.day.offset(timescale.domain()[1], 1))
};
// scales
var y = d3.scale.linear().domain([0, max_total_counts]).rangeRound([height, 0]),
navY = d3.scale.linear().domain([0, max_total_counts]).rangeRound([navHeight, 0]),
time = d3.time.scale().domain(max_extent_dates).range([0, width]),
navTime = d3.time.scale().domain(max_extent_dates).range([0, width]),
x = d3.scale.ordinal().domain(max_extent_in_days(time)).rangeBands([0, width], 0.1, 0), // used to calculate bar widths
navX = d3.scale.ordinal().domain(max_extent_in_days(navTime)).rangeBands([0, width], 0.1, 0),
y2 = d3.scale.linear().domain([0, d3.max(data.badge_set_reached, function(d) {
return d.set
})]).rangeRound([height, 0]);
navY2 = d3.scale.linear().domain([0, d3.max(data.badge_set_reached, function(d) {
return d.set
})]).rangeRound([navHeight, 0]);
// axes
var x_axis = d3.svg.axis().scale(time).orient('bottom') // .tickFormat(d3.time.format('%Y-%m-%d'))
.outerTickSize(0), // at start and end of axis line
nav_x_axis = d3.svg.axis().scale(navTime).orient('bottom') // .tickFormat(d3.time.format('%Y-%m-%d'))
.outerTickSize(0), // at start and end of axis line
y_axis = d3.svg.axis().scale(y).orient('left').tickFormat(d3.format('d'));
y2_axis = d3.svg.axis().scale(y2).orient('right').tickFormat(d3.format('d'));
// add brush
var brush = d3.svg.brush()
.x(navTime)
.on("brush", brushed);
// svg context
var svg = d3.select("#milestones_attempts_combo")
.append('svg')
.attr('class', 'chart')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append('g')
.attr('class', 'focus')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var context = svg.append('g')
.attr('class', 'context')
.attr('transform', 'translate(' + navMargin.left + ',' + navMargin.top + ')');
// Plot the stacked bars
var focus_bar = focus
.append("g")
.attr("class","barClipper")
.style('clip-path', 'url(#clip)')
.selectAll('.g')
.data(data.answer_counts)
.enter().append('g')
.attr('class', 'g bar stack')
.attr('transform', function(d) {
return "translate(" + x(d.date) + ",0)"
});
var focus_rects = focus_bar.selectAll('rect')
.data(function(d) {
return d.ys;
})
.enter().append('rect')
.attr('width', x.rangeBand())
.attr('height', function(d) {
return y(d.y0) - y(d.y1);
})
.attr('y', function(d) {
return y(d.y1);
})
.attr('class', function(d) {
return 'rect ' + d['class'];
});
var context_bar = context.selectAll('.g')
.data(data.answer_counts)
.enter().append('g')
.attr('class', 'g')
.attr('transform', function(d) {
return "translate(" + navTime(d.date) + ",0)"
});
var context_rects = context_bar.selectAll('rect')
.data(function(d) {
return d.ys;
})
.enter().append('rect')
.attr('width', navX.rangeBand())
.attr('height', function(d) {
return navY(d.y0) - navY(d.y1);
})
.attr('y', function(d) {
return navY(d.y1);
})
.attr('class', function(d) {
return 'rect ' + d['class'];
});
// Plot lines
var line = d3.svg.line()
.x(function(d) {
return time(d.date)
})
.y(function(d) {
return y2(d.set)
})
.interpolate('step-after');
var focus_line = focus.append('path')
.datum(data.badge_set_reached)
.attr('class', 'line focus-line')
.attr('d', line);
var line2 = d3.svg.line()
.x(function(d) {
return time(d.date)
})
.y(function(d) {
return navY2(d.set)
})
.interpolate('step-after');
var context_line = context.append('path')
.datum(data.badge_set_reached)
.attr('class', 'line context-line')
.attr('d', line2);
// Plot axes
focus.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0, ' + height + ')')
.call(x_axis)
.selectAll('text')
.style('text-anchor', 'end')
// .attr('transform', 'rotate(-45)')
// .attr('dx', '-.5em')
// .attr('dy', '.5em');
focus.append('g')
.attr('class', 'y axis')
.attr('transform', 'translate(0, 0)')
.call(y_axis);
focus.append('g')
.attr('class', 'y2 axis')
.attr('transform', 'translate(' + width + ', 0)')
.call(y2_axis);
context.append('g')
.attr('class', 'navX axis')
.attr('transform', 'translate(0, ' + navHeight + ')')
.call(nav_x_axis);
// Label axes
svg.append('text')
.attr('class', 'label y-axis-label')
.attr('transform', 'rotate(-90)')
.attr('x', 0 - ((height + margin.top) / 2))
.attr('y', 0)
.attr('dy', 20)
.style('text-anchor', 'middle')
.text('Paths Attempted');
svg.append('text')
.attr('class', 'label y2-axis-label')
.attr('transform', 'rotate(+90)')
.attr('y', 0 - (width + margin.left + margin.right))
.attr('x', 0 + ((height + margin.top) / 2))
.attr('dy', '2em')
.style('text-anchor', 'middle')
.text('Badge Set Reached');
// Add brush to svg
context.append('g')
.attr('class', 'x brush')
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", navHeight + 7);
function brushed() {
time.domain(brush.empty() ? navTime.domain() : brush.extent())
.range([0, (width)]);
x.domain(max_extent_in_days(time))
.rangeBands([margin.left, (width)], 0.1, 0);
focus.selectAll('.bar.stack')
.attr('transform', function(d) {
return "translate(" + time(d.date) + ",0)";
})
.attr('width', x.rangeBand());
focus.selectAll('.rect')
.attr('width', x.rangeBand());
focus.selectAll('.line').attr('d', line);
focus.select(".x.axis").call(x_axis);
};
})();
</script>
</body>
</html>