1 /**
2     Gets the element at index n of a range
3 */
4 module ddash.range.nth;
5 
6 ///
7 unittest {
8     import optional: some, none;
9     import ddash.range.front;
10     assert([1, 2].nth(1).frontOr(1) == 2);
11     assert((int[]).init.nth(1).frontOr(9) == 9);
12 
13     assert([1, 2].nth(1) == some(2));
14     assert((int[]).init.nth(1) == none);
15 
16     assert([1, 2, 3].nth!(Yes.wrap)(10) == some(2));
17 }
18 
19 import ddash.common;
20 
21 /**
22     Gets the element at index n of array if found, else `none`.
23 
24     Params:
25         wrap = If `Yes.wrap`, then we wrap around the edge, else not
26         range = an input range
27         n = which element to return
28 
29     Returns
30         The value at the nth index of range or defaultValue i not found
31 
32     Since:
33         0.0.1
34 */
35 auto nth(
36     from!"std.typecons".Flag!"wrap" wrap = from!"std.typecons".No.wrap,
37     Range,
38 )(
39     Range range,
40     size_t n,
41 )
42 if (from!"std.range".isInputRange!Range)
43 {
44 
45     import std.range: empty, walkLength, isRandomAccessRange, ElementType;
46     import std.typecons: Yes;
47     import optional: no, some;
48 
49     alias T = ElementType!Range;
50 
51     if (range.empty) {
52         return no!T;
53     }
54     auto length = range.walkLength;
55     static if (isRandomAccessRange!Range)
56     {
57         alias get = a => range[a];
58     }
59     else
60     {
61         import std.range: dropExactly;
62         alias get = a => range
63             .dropExactly(a)
64             .front;
65     }
66 
67     static if (wrap == Yes.wrap)
68     {
69         return some!T(get(n % length));
70     }
71     else
72     {
73         if (n >= length) {
74             return no!T;
75         }
76         return some!T(get(n));
77     }
78 }
79 
80 unittest {
81     import std.algorithm: filter;
82     import optional: some, none;
83     assert([1, 2].nth(1) == some(2));
84     assert([1, 2].filter!"true".nth(1) == some(2));
85     assert((int[]).init.nth(1) == none);
86     assert([1, 2].nth!(No.wrap)(2) == none);
87     assert([1, 2].nth!(Yes.wrap)(2) == some(1));
88 }
89 
90 unittest {
91     const(string)[] args = [];
92     static assert(__traits(compiles, { args.nth(0); }));
93 }
94 
95 /**
96     Returns `optional` front of range
97 
98     Since:
99         - 0.0.1
100 */
101 alias first = from!"ddash.range".maybeFront;
102 
103 ///
104 unittest {
105     import optional: some, none;
106     assert([1, 2].first == some(1));
107     assert((int[]).init.first == none);
108 }
109 
110 /**
111     Returns `optional` end of range
112 
113     Since:
114         - 0.0.1
115 */
116 alias last = from!"ddash.range".maybeBack;
117 
118 ///
119 unittest {
120     import optional: some, none;
121     assert([1, 2].last == some(2));
122     assert((int[]).init.last == none);
123 }