Coverage for src/toolbox_python/lists.py: 100%

14 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-13 07:24 +0000

1# ============================================================================ # 

2# # 

3# Title : Lists # 

4# Purpose : Manipulate and enhance lists. # 

5# # 

6# ============================================================================ # 

7 

8 

9# ---------------------------------------------------------------------------- # 

10# # 

11# Overview #### 

12# # 

13# ---------------------------------------------------------------------------- # 

14 

15 

16# ---------------------------------------------------------------------------- # 

17# Description #### 

18# ---------------------------------------------------------------------------- # 

19 

20 

21""" 

22!!! note "Summary" 

23 The `lists` module is used to manipulate and enhance Python `#!py list`'s. 

24!!! abstract "Details" 

25 Note that functions in this module will only take-in and manipulate existing `#!py list` objects, and also output `#!py list` objects. It will not sub-class the base `#!py list` object, or create new '`#!py list`-like' objects. It will always maintain pure python types at it's core. 

26""" 

27 

28 

29# ---------------------------------------------------------------------------- # 

30# # 

31# Setup #### 

32# # 

33# ---------------------------------------------------------------------------- # 

34 

35 

36# ---------------------------------------------------------------------------- # 

37# Imports #### 

38# ---------------------------------------------------------------------------- # 

39 

40 

41# ## Python StdLib Imports ---- 

42from itertools import product as itertools_product 

43from typing import Any, Optional, Union 

44 

45# ## Python Third Party Imports ---- 

46from more_itertools import collapse as itertools_collapse 

47from typeguard import typechecked 

48 

49# ## Local First Party Imports ---- 

50from toolbox_python.collection_types import any_list, any_tuple, collection, scalar 

51 

52 

53# ---------------------------------------------------------------------------- # 

54# Exports #### 

55# ---------------------------------------------------------------------------- # 

56 

57__all__: list[str] = ["flatten", "flat_list", "product"] 

58 

59 

60# ---------------------------------------------------------------------------- # 

61# # 

62# Functions #### 

63# # 

64# ---------------------------------------------------------------------------- # 

65 

66 

67@typechecked 

68def flatten( 

69 list_of_lists: Union[scalar, collection], 

70 base_type: Optional[type] = None, 

71 levels: Optional[int] = None, 

72) -> any_list: 

73 """ 

74 !!! note "Summary" 

75 For a given `#!py list` of `#!py list`'s, flatten it out to be a single `#!py list`. 

76 

77 ???+ info "Details" 

78 Under the hood, this function will call the [`#!py more_itertools.collapse()`][more_itertools.collapse] function. The difference between this function and the [`#!py more_itertools.collapse()`][more_itertools.collapse] function is that the one from [`#!py more_itertools`][more_itertools] will return a `chain` object, not a `list` object. So, all we do here is call the [`#!py more_itertools.collapse()`][more_itertools.collapse] function, then parse the result in to a `#!py list()` function to ensure that the result is always a `#!py list` object. 

79 

80 [more_itertools]: https://more-itertools.readthedocs.io/en/stable/api.html 

81 [more_itertools.collapse]: https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.collapse 

82 

83 Params: 

84 list_of_lists (list[any_list]): 

85 The input `#!py list` of `#!py list`'s that you'd like to flatten to a single-level `#!py list`. 

86 base_type (Optional[type], optional): 

87 Binary and text strings are not considered iterable and will not be collapsed. To avoid collapsing other types, specify `base_type`.<br> 

88 Defaults to `#!py None`. 

89 levels (Optional[int], optional): 

90 Specify `levels` to stop flattening after a certain nested level.<br> 

91 Defaults to `#!py None`. 

92 

93 Raises: 

94 TypeError: If any of the inputs parsed to the parameters of this function are not the correct type. Uses the [`@typeguard.typechecked`](https://typeguard.readthedocs.io/en/stable/api.html#typeguard.typechecked) decorator. 

95 

96 Returns: 

97 (any_list): 

98 The updated `#!py list`. 

99 

100 ???+ example "Examples" 

101 

102 ```{.py .python linenums="1" title="Set up"} 

103 >>> from toolbox_python.lists import flatten 

104 ``` 

105 

106 ```{.py .python linenums="1" title="Example 1: Basic list, same input & output"} 

107 >>> print(flatten([0, 1, 2, 3])) 

108 ``` 

109 <div class="result" markdown> 

110 ```{.sh .shell title="Terminal"} 

111 [0, 1, 2, 3] 

112 ``` 

113 !!! success "Conclusion: Successful flattening." 

114 </div> 

115 

116 ```{.py .python linenums="1" title="Example 2: List containing two lists"} 

117 >>> print(flatten([[0, 1], [2, 3]])) 

118 ``` 

119 <div class="result" markdown> 

120 ```{.sh .shell title="Terminal"} 

121 [0, 1, 2, 3] 

122 ``` 

123 !!! success "Conclusion: Successful flattening." 

124 </div> 

125 

126 ```{.py .python linenums="1" title="Example 3: List containing a list and other data"} 

127 >>> print(flatten([0, 1, [2, 3]])) 

128 ``` 

129 <div class="result" markdown> 

130 ```{.sh .shell title="Terminal"} 

131 [0, 1, 2, 3] 

132 ``` 

133 !!! success "Conclusion: Successful flattening." 

134 </div> 

135 

136 ```{.py .python linenums="1" title="Example 4: List containing two lists and other data"} 

137 >>> print(flatten([[0, 1], [2, 3], 4, 5])) 

138 ``` 

139 <div class="result" markdown> 

140 ```{.sh .shell title="Terminal"} 

141 [0, 1, 2, 3, 4, 5] 

142 ``` 

143 !!! success "Conclusion: Successful flattening." 

144 </div> 

145 

146 ```{.py .python linenums="1" title="Example 5: List containing a list, a tuple, and other data"} 

147 >>> print(flatten([[0, 1], (2, 3), 4, 5])) 

148 ``` 

149 <div class="result" markdown> 

150 ```{.sh .shell title="Terminal"} 

151 [0, 1, 2, 3, 4, 5] 

152 ``` 

153 !!! success "Conclusion: Successful flattening." 

154 </div> 

155 

156 ```{.py .python linenums="1" title="Example 6: List containing up to three levels deep"} 

157 >>> print(flatten([[0, 1], [2, 3, [4, 5]]])) 

158 ``` 

159 <div class="result" markdown> 

160 ```{.sh .shell title="Terminal"} 

161 [0, 1, 2, 3, 4, 5] 

162 ``` 

163 !!! success "Conclusion: Successful flattening." 

164 </div> 

165 

166 ```{.py .python linenums="1" title="Example 7: List containing up to three levels deep, plus other data"} 

167 >>> print(flatten([[0, 1], [2, 3, [4, 5]], 6, 7])) 

168 ``` 

169 <div class="result" markdown> 

170 ```{.sh .shell title="Terminal"} 

171 [0, 1, 2, 3, 4, 5, 6, 7] 

172 ``` 

173 !!! success "Conclusion: Successful flattening." 

174 </div> 

175 

176 ```{.py .python linenums="1" title="Example 8: List containing up to four levels deep"} 

177 >>> print(flatten([[0, 1], [2, 3, [4, [5]]]])) 

178 ``` 

179 <div class="result" markdown> 

180 ```{.sh .shell title="Terminal"} 

181 [0, 1, 2, 3, 4, 5] 

182 ``` 

183 !!! success "Conclusion: Successful flattening." 

184 </div> 

185 

186 ??? tip "See Also" 

187 - [`more_itertools`](https://more-itertools.readthedocs.io/en/stable/api.html) 

188 - [`more_itertools.collapse()`](https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.collapse) 

189 """ 

190 return list( 

191 itertools_collapse( 

192 iterable=list_of_lists, 

193 base_type=base_type, 

194 levels=levels, 

195 ) 

196 ) 

197 

198 

199@typechecked 

200def flat_list(*inputs: Any) -> any_list: 

201 """ 

202 !!! note "Summary" 

203 Take in any number of inputs, and output them all in to a single flat `#!py list`. 

204 

205 Params: 

206 inputs (Any): 

207 Any input. 

208 

209 Raises: 

210 TypeError: If any of the inputs parsed to the parameters of this function are not the correct type. Uses the [`@typeguard.typechecked`](https://typeguard.readthedocs.io/en/stable/api.html#typeguard.typechecked) decorator. 

211 

212 Returns: 

213 (any_list): 

214 The input having been coerced to a single flat `#!py list`. 

215 

216 ???+ example "Examples" 

217 

218 ```{.py .python linenums="1" title="Set up"} 

219 >>> from toolbox_python.lists import flat_list 

220 ``` 

221 

222 ```{.py .python linenums="1" title="Example 1: Basic input & output"} 

223 >>> print(flat_list(0, 1, 2, 3)) 

224 ``` 

225 <div class="result" markdown> 

226 ```{.sh .shell title="Terminal"} 

227 [0, 1, 2, 3] 

228 ``` 

229 !!! success "Conclusion: Successful flattening." 

230 </div> 

231 

232 ```{.py .python linenums="1" title="Example 2: Multiple lists"} 

233 >>> print(flat_list([0, 1], [2, 3])) 

234 ``` 

235 <div class="result" markdown> 

236 ```{.sh .shell title="Terminal"} 

237 [0, 1, 2, 3] 

238 ``` 

239 !!! success "Conclusion: Successful flattening." 

240 </div> 

241 

242 ```{.py .python linenums="1" title="Example 3: List and other data"} 

243 >>> print(flat_list(0, 1, [2, 3])) 

244 ``` 

245 <div class="result" markdown> 

246 ```{.sh .shell title="Terminal"} 

247 [0, 1, 2, 3] 

248 ``` 

249 !!! success "Conclusion: Successful flattening." 

250 </div> 

251 

252 ```{.py .python linenums="1" title="Example 4: Multiple lists and other data"} 

253 >>> print(flat_list([0, 1], [2, 3], 4, 5)) 

254 ``` 

255 <div class="result" markdown> 

256 ```{.sh .shell title="Terminal"} 

257 [0, 1, 2, 3, 4, 5] 

258 ``` 

259 !!! success "Conclusion: Successful flattening." 

260 </div> 

261 

262 ```{.py .python linenums="1" title="Example 5: List and a tuple and other data"} 

263 >>> print(flat_list([0, 1], (2, 3), 4, 5)) 

264 ``` 

265 <div class="result" markdown> 

266 ```{.sh .shell title="Terminal"} 

267 [0, 1, 2, 3, 4, 5] 

268 ``` 

269 !!! success "Conclusion: Successful flattening." 

270 </div> 

271 

272 ```{.py .python linenums="1" title="Example 6: List and a nested list"} 

273 >>> print(flat_list([0, 1], [2, 3, [4, 5]])) 

274 ``` 

275 <div class="result" markdown> 

276 ```{.sh .shell title="Terminal"} 

277 [0, 1, 2, 3, 4, 5] 

278 ``` 

279 !!! success "Conclusion: Successful flattening." 

280 </div> 

281 

282 ```{.py .python linenums="1" title="Example 7: List and a nested list and other data"} 

283 >>> print(flat_list([0, 1], [2, 3, [4, 5]], 6, 7)) 

284 ``` 

285 <div class="result" markdown> 

286 ```{.sh .shell title="Terminal"} 

287 [0, 1, 2, 3, 4, 5, 6, 7] 

288 ``` 

289 !!! success "Conclusion: Successful flattening." 

290 </div> 

291 

292 ```{.py .python linenums="1" title="Example 8: Deep nested lists"} 

293 >>> print(flat_list([0, 1], [2, 3, [4, [5]]])) 

294 ``` 

295 <div class="result" markdown> 

296 ```{.sh .shell title="Terminal"} 

297 [0, 1, 2, 3, 4, 5] 

298 ``` 

299 !!! success "Conclusion: Successful flattening." 

300 </div> 

301 

302 ??? tip "See Also" 

303 - [`flatten()`][toolbox_python.lists.flatten] 

304 """ 

305 return flatten(list(inputs)) 

306 

307 

308def product(*iterables) -> list[any_tuple]: 

309 """ 

310 !!! note "Summary" 

311 For a given number of `#!py iterables`, perform a cartesian product on them, and return the result as a list. 

312 

313 ???+ info "Details" 

314 Under the hood, this function will call the [`#!py itertools.product()`][itertools.product] function. The difference between this function and the [`#!py itertools.product()`][itertools.product] function is that the one from [`#!py itertools`][itertools] will return a `product` object, not a `list` object. So, all we do here is call the [`#!py itertools.product()`][itertools.product] function, then parse the result in to a `#!py list()` function to ensure that the result is always a `#!py list` object. 

315 

316 [itertools]: https://docs.python.org/3/library/itertools.html 

317 [itertools.product]: https://docs.python.org/3/library/itertools.html#itertools.product 

318 

319 Params: 

320 iterables (Any): 

321 The input `#!py iterables` that you'd like to expand out. 

322 

323 Returns: 

324 (list[tuple[Any, ...]]): 

325 The updated `#!py list` list of `#!py tuple`'s representing the Cartesian product of the provided iterables. 

326 

327 ???+ example "Examples" 

328 

329 ```{.py .python linenums="1" title="Set up"} 

330 >>> from toolbox_python.lists import product 

331 ``` 

332 

333 ```{.py .python linenums="1" title="Example 1: Basic input & output"} 

334 >>> print(product([1], [11], [111])) 

335 ``` 

336 <div class="result" markdown> 

337 ```{.sh .shell title="Terminal"} 

338 [ 

339 (1, 11, 111), 

340 ] 

341 ``` 

342 !!! success "Conclusion: Successful conversion." 

343 </div> 

344 

345 ```{.py .python linenums="1" title="Example 2: Multiple lists"} 

346 >>> print(product([1, 2], [11], [111])) 

347 ``` 

348 <div class="result" markdown> 

349 ```{.sh .shell title="Terminal"} 

350 [ 

351 (1, 11, 111), 

352 (2, 11, 111), 

353 ] 

354 ``` 

355 !!! success "Conclusion: Successful conversion." 

356 </div> 

357 

358 ```{.py .python linenums="1" title="Example 3: List and other data"} 

359 >>> print(product([1, 2], [11], [111, 222])) 

360 ``` 

361 <div class="result" markdown> 

362 ```{.sh .shell title="Terminal"} 

363 [ 

364 (1, 11, 111), 

365 (1, 11, 222), 

366 (2, 11, 111), 

367 (2, 11, 222), 

368 ] 

369 ``` 

370 !!! success "Conclusion: Successful conversion." 

371 </div> 

372 

373 ```{.py .python linenums="1" title="Example 4: Multiple lists and other data"} 

374 >>> print(product([1, 2], [11, 22], [111, 222])) 

375 ``` 

376 <div class="result" markdown> 

377 ```{.sh .shell title="Terminal"} 

378 [ 

379 (1, 11, 111), 

380 (1, 11, 222), 

381 (1, 22, 111), 

382 (1, 22, 222), 

383 (2, 11, 111), 

384 (2, 11, 222), 

385 (2, 22, 111), 

386 (2, 22, 222), 

387 ] 

388 ``` 

389 !!! success "Conclusion: Successful conversion." 

390 </div> 

391 

392 ??? tip "See Also" 

393 - [itertools](https://docs.python.org/3/library/itertools.html) 

394 - [itertools.product()](https://docs.python.org/3/library/itertools.html#itertools.product) 

395 """ 

396 return list(itertools_product(*iterables))