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

14 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-02-24 10:34 +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 ( 

51 any_list, 

52 any_tuple, 

53 collection, 

54 scalar, 

55 str_list, 

56) 

57 

58 

59# ---------------------------------------------------------------------------- # 

60# Exports #### 

61# ---------------------------------------------------------------------------- # 

62 

63__all__: str_list = ["flatten", "flat_list", "product"] 

64 

65 

66# ---------------------------------------------------------------------------- # 

67# # 

68# Functions #### 

69# # 

70# ---------------------------------------------------------------------------- # 

71 

72 

73@typechecked 

74def flatten( 

75 list_of_lists: Union[scalar, collection], 

76 base_type: Optional[type] = None, 

77 levels: Optional[int] = None, 

78) -> any_list: 

79 """ 

80 !!! note "Summary" 

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

82 

83 ???+ info "Details" 

84 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. 

85 

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

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

88 

89 Params: 

90 list_of_lists (list[any_list]): 

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

92 base_type (Optional[type], optional): 

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

94 Defaults to `#!py None`. 

95 levels (Optional[int], optional): 

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

97 Defaults to `#!py None`. 

98 

99 Raises: 

100 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. 

101 

102 Returns: 

103 (any_list): 

104 The updated `#!py list`. 

105 

106 ???+ example "Examples" 

107 

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

109 >>> from toolbox_python.lists import flatten 

110 ``` 

111 

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

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

114 ``` 

115 <div class="result" markdown> 

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

117 [0, 1, 2, 3] 

118 ``` 

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

120 </div> 

121 

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

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

124 ``` 

125 <div class="result" markdown> 

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

127 [0, 1, 2, 3] 

128 ``` 

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

130 </div> 

131 

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

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

134 ``` 

135 <div class="result" markdown> 

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

137 [0, 1, 2, 3] 

138 ``` 

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

140 </div> 

141 

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

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

144 ``` 

145 <div class="result" markdown> 

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

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

148 ``` 

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

150 </div> 

151 

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

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

154 ``` 

155 <div class="result" markdown> 

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

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

158 ``` 

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

160 </div> 

161 

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

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

164 ``` 

165 <div class="result" markdown> 

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

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

168 ``` 

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

170 </div> 

171 

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

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

174 ``` 

175 <div class="result" markdown> 

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

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

178 ``` 

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

180 </div> 

181 

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

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

184 ``` 

185 <div class="result" markdown> 

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

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

188 ``` 

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

190 </div> 

191 

192 ??? tip "See Also" 

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

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

195 """ 

196 return list( 

197 itertools_collapse( 

198 iterable=list_of_lists, # type: ignore 

199 base_type=base_type, 

200 levels=levels, 

201 ) 

202 ) 

203 

204 

205@typechecked 

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

207 """ 

208 !!! note "Summary" 

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

210 

211 Params: 

212 inputs (Any): 

213 Any input. 

214 

215 Raises: 

216 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. 

217 

218 Returns: 

219 (any_list): 

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

221 

222 ???+ example "Examples" 

223 

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

225 >>> from toolbox_python.lists import flat_list 

226 ``` 

227 

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

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

230 ``` 

231 <div class="result" markdown> 

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

233 [0, 1, 2, 3] 

234 ``` 

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

236 </div> 

237 

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

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

240 ``` 

241 <div class="result" markdown> 

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

243 [0, 1, 2, 3] 

244 ``` 

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

246 </div> 

247 

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

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

250 ``` 

251 <div class="result" markdown> 

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

253 [0, 1, 2, 3] 

254 ``` 

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

256 </div> 

257 

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

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

260 ``` 

261 <div class="result" markdown> 

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

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

264 ``` 

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

266 </div> 

267 

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

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

270 ``` 

271 <div class="result" markdown> 

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

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

274 ``` 

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

276 </div> 

277 

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

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

280 ``` 

281 <div class="result" markdown> 

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

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

284 ``` 

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

286 </div> 

287 

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

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

290 ``` 

291 <div class="result" markdown> 

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

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

294 ``` 

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

296 </div> 

297 

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

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

300 ``` 

301 <div class="result" markdown> 

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

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

304 ``` 

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

306 </div> 

307 

308 ??? tip "See Also" 

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

310 """ 

311 return flatten(list(inputs)) 

312 

313 

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

315 """ 

316 !!! note "Summary" 

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

318 

319 ???+ info "Details" 

320 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. 

321 

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

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

324 

325 Params: 

326 iterables (Any): 

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

328 

329 Returns: 

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

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

332 

333 ???+ example "Examples" 

334 

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

336 >>> from toolbox_python.lists import product 

337 ``` 

338 

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

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

341 ``` 

342 <div class="result" markdown> 

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

344 [ 

345 (1, 11, 111), 

346 ] 

347 ``` 

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

349 </div> 

350 

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

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

353 ``` 

354 <div class="result" markdown> 

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

356 [ 

357 (1, 11, 111), 

358 (2, 11, 111), 

359 ] 

360 ``` 

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

362 </div> 

363 

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

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

366 ``` 

367 <div class="result" markdown> 

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

369 [ 

370 (1, 11, 111), 

371 (1, 11, 222), 

372 (2, 11, 111), 

373 (2, 11, 222), 

374 ] 

375 ``` 

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

377 </div> 

378 

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

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

381 ``` 

382 <div class="result" markdown> 

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

384 [ 

385 (1, 11, 111), 

386 (1, 11, 222), 

387 (1, 22, 111), 

388 (1, 22, 222), 

389 (2, 11, 111), 

390 (2, 11, 222), 

391 (2, 22, 111), 

392 (2, 22, 222), 

393 ] 

394 ``` 

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

396 </div> 

397 

398 ??? tip "See Also" 

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

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

401 """ 

402 return list(itertools_product(*iterables))