· 4 years ago · Apr 27, 2021, 12:02 AM
1/* ************************************************************************
2 * In this exercise, you're given a few functions simulating API *
3 * endpoints. Your task is to use them to write a function that retrieves *
4 * a list of investable properties. Any property belonging to an *
5 * investable region, or an investable region's descendant in the *
6 * regions-tree, is considered investable. Please write the most *
7 * performant but readable code you can (assuming the code will be *
8 * deployed to production), without modifying any of the API functions or *
9 * the signature of `getInvestableProperties`. (You are allowed to add *
10 * your own helper functions though.) You can use the *
11 * displayResults(data) helper as a sample callback. Your code should run *
12 * on Node v10.16. *
13 * ************************************************************************ */
14
15/* ************************************************************************
16 * AVAILABLE API FUNCTIONS -- DO NOT MODIFY THIS SECTION *
17 * ************************************************************************ */
18
19function waitFor(milliseconds) {
20 return new Promise((resolve) => {
21 setTimeout(resolve, milliseconds);
22 });
23}
24
25/* ************************************************************************
26 * Returns a promise that returns an array of region objects that contain *
27 * a parent region ("" when at the top level), creating a hierarchical *
28 * tree of data. Responses can take anywhere up to 1.5s to return. This *
29 * example uses a tree 4 levels deep, but your result will be tested *
30 * against a datafile that contains an unknown number of layers. *
31 * ************************************************************************ */
32
33async function getAllRegions() {
34 const regions = {
35 regions: [
36 { name: 'blackrod', parent: 'bolton' },
37 { name: 'bolton', parent: 'manchester' },
38 { name: 'bury', parent: 'manchester' },
39 { name: 'camden', parent: 'central london' },
40 { name: 'camden town', parent: 'camden' },
41 { name: 'central london', parent: 'london' },
42 { name: 'covent garden', parent: 'westminster' },
43 { name: 'croydon', parent: 'south-west london' },
44 { name: 'east london', parent: 'london' },
45 { name: 'farnworth', parent: 'bolton' },
46 { name: 'hatton garden', parent: 'camden' },
47 { name: 'heywood', parent: 'rochdale' },
48 { name: 'holborn', parent: 'camden' },
49 { name: 'kensington and chelsea', parent: 'london' },
50 { name: 'kew', parent: 'richmond upon thames' },
51 { name: 'kingston upon thames', parent: 'south-west london' },
52 { name: 'london', parent: '' },
53 { name: 'manchester', parent: '' },
54 { name: 'middleton', parent: 'rochdale' },
55 { name: 'north london', parent: 'london' },
56 { name: 'oldham', parent: 'manchester' },
57 { name: 'richmond upon thames', parent: 'south-west london' },
58 { name: 'rochdale', parent: 'manchester' },
59 { name: 'south london', parent: 'london' },
60 { name: 'south-west london', parent: 'london' },
61 { name: 'twickenham', parent: 'richmond upon thames' },
62 { name: 'west london', parent: 'london' },
63 { name: 'westminster', parent: 'central london' },
64 { name: 'wimbledon', parent: 'south-west london' },
65 ],
66 };
67
68 await waitFor(Math.random() * 1500);
69
70 return regions;
71}
72
73/* ************************************************************************
74 * Takes as input a comma separated list of permissible regions. Will *
75 * return a promise that returns all properties that match any of the *
76 * regions provided (but will not make any assumptions about sub-regions: *
77 * e.g. "london" will not return a match for "twickenham" even though *
78 * Twickenham is within the London region. Timing varies with the number *
79 * of provided regions. Assume that there are some regions that cause *
80 * unexpected errors which have not yet been investigated. *
81 * ************************************************************************ */
82async function getPropertiesByRegion(regions) {
83 const properties = {
84 properties: [
85 { address: 'Whitton Rd, Twickenham TW2 7BA', region: 'twickenham' },
86 { address: 'Royal Botanic Gardens, Kew, Richmond, Surrey, TW9 3AE', region: 'kew' },
87 { address: 'Plough Ln, London SW17 0BL', region: 'wimbledon' },
88 { address: 'Stables Market, Chalk Farm Road, London NW1', region: 'camden town' },
89 { address: 'Westminster, London SW1A 0AA', region: 'westminster' },
90 { address: 'The Esplanade, Rochdale OL16 1AQ', region: 'rochdale' },
91 { address: 'The Old Town Hall, Parliament Square, Greaves Street, Oldham, OL1 1QN', region: 'oldham' },
92 { address: 'Castle House, Castle Rd, Bury BL9 8QT', region: 'bury' },
93 ],
94 };
95
96 const properties_to_return = {
97 properties: [],
98 };
99
100 const array_of_regions = regions.split(',');
101 const number_of_regions_requested = (array_of_regions.length || 1);
102
103 for (const desired_region of array_of_regions) {
104 const matching_properties = properties.properties.filter(record => record.region === desired_region);
105 properties_to_return.properties.push(...matching_properties);
106 }
107
108 await waitFor(Math.random() * 1000 * number_of_regions_requested);
109
110 return properties_to_return;
111}
112
113/* ************************************************************************
114 * Will return a promise that returns all subregions in which investable *
115 * properties are contained. You should not assume that these are all *
116 * leaf-elements within the hierarchy of the region tree. *
117 * ************************************************************************ */
118async function getInvestableRegions() {
119 const investable_regions = {
120 regions: [ 'camden', 'kew', 'rochdale' ],
121 };
122
123 await waitFor(Math.random() * 3000);
124
125 return investable_regions;
126}
127
128function displayResults(data) {
129 console.log(data);
130}
131
132/* ************************************************************************
133 * END OF API IMPLEMENTATION *
134 * ************************************************************************ */
135
136
137/* ************************************************************************
138 * PLEASE IMPLEMENT YOUR RESPONSE BELOW: *
139 * ************************************************************************ */
140
141 /* ************************************************************************
142 * Please write the most performant but readable code you can (assuming *
143 * the code will be deployed to production), without modifying any of the *
144 * functions above and the signature of the following function, that will *
145 * return a promise that returns an object that has the "properties" key *
146 * whose value is an array of properties - { properties: [ {...}, ... ] } *
147 * The properties should be contained in the specified region or any *
148 * sub-region (or sub-sub-region down to an unknown nth level) that is *
149 * within the "investable" regions (e.g. calling your method with *
150 * "manchester" should not return results for "bury"). Your method *
151 * should take as input a region (e.g. "london" or "wimbledon"). You can *
152 * use the displayResults(data) helper as a sample callback. Your code *
153 * should run on Node v10.16. *
154 * ************************************************************************ */
155
156function getInvestableProperties(top_level_region) {
157
158 return new Promise((resolve) => {
159
160
161 getInvestableRegions().then(function(allInvestableRegions){
162 getAllRegions().then(function(allRegions){
163
164 //Get all available sub-sub regions
165 allSubRegions = getAllSubRegions(allRegions,top_level_region)
166 var investableRegions = "";
167 for (const desired_region of allSubRegions.regions) {
168 if (allInvestableRegions.regions.indexOf(desired_region.name) === -1) {
169 //Does not exist in investable regions
170 continue;
171 }else {
172 //Exists in investable regions
173 investableRegions += desired_region.name+",";
174 }
175 }
176 if (investableRegions==""){
177 resolve({properties: [],});
178 }
179 getPropertiesByRegion(investableRegions).then(function(investableProperties){
180 resolve(investableProperties);
181 }); //end getPropertiesByRegion()
182
183 }); //end getAllRegions()
184
185 }); //end getInvestableRegions()
186 }); //end Promise
187
188}
189
190const allSubRegionsToReturn = {
191 regions: [],
192};
193
194function getAllSubRegions(allRegions,top_level_region) {
195 subRegions = allRegions.regions.filter(record => record.parent === top_level_region);
196
197 if(subRegions.length==0) {
198 return {
199 regions: [],
200 };
201 }
202
203 allSubRegionsToReturn.regions.push(...subRegions);
204
205 for (const desired_region of subRegions) {
206
207 getAllSubRegions(allRegions,desired_region.name);
208
209 }
210 return allSubRegionsToReturn;
211}
212
213/* helpers to get you started */
214// getAllRegions().then(displayResults);
215// getInvestableRegions().then(displayResults);
216// getPropertiesByRegion('twickenham').then(displayResults);
217 getInvestableProperties('london').then(displayResults);