1 /**
2  * Copyright: Copyright (c) 2008-2009 Jacob Carlborg. All rights reserved.
3  * Authors: Jacob Carlborg
4  * Version: Initial created: 2008
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  *
7  */
8 module mambo.core.Array;
9 
10 import stdArray = std.array;
11 import algorithm = std.algorithm;
12 import stdRange = std.range;
13 import stdString = std.string;
14 
15 static import tango.core.Array;
16 import tango.stdc.string : memmove;
17 static import tango.text.Util;
18 
19 import mambo.core.AssociativeArray;
20 import mambo.core.core;
21 import mambo.util.Traits;
22 
23 alias stdArray.array toArray;
24 alias algorithm.canFind contains;
25 alias algorithm.count count;
26 alias algorithm.countUntil indexOf;
27 alias algorithm.filter filter;
28 alias stdArray.join join;
29 alias algorithm.sort sort;
30 alias algorithm.startsWith startsWith;
31 alias algorithm.uniq unique;
32 
33 alias stdRange.chain append;
34 alias stdArray.split split;
35 
36 /**
37  * Inserts the given element(s) or range at the given position into the array. Shifts the
38  * element currently at that position (if any) and any subsequent elements to the right.
39  *
40  * Params:
41  *     arr = the array to insert the element(s) or range into
42  *     index = the index at which the specified element(s) or range is to be inserted to
43  *     r = the element(s) or range to be inserted
44  *
45  * Returns: a copy of the given array with the element(s) or range inserted
46  */
47 T[] insert (T, RangeOrElement...) (T[] arr, size_t index, RangeOrElement r)
48 {
49 	auto copy = arr.dup;
50 	stdArray.insertInPlace(copy, index, r);
51 	return copy;
52 }
53 
54 /**
55 * Inserts the given element(s) or range, in place, at the given position into the array.
56 * Shifts the element currently at that position (if any) and any subsequent elements to the
57 * right.
58 *
59 * This will modify the given array in place.
60 *
61 * Params:
62 *     arr = the array to insert the element(s) or range into
63 *     index = the index at which the specified element(s) or range is to be inserted to
64 *     r = the element(s) or range to be inserted
65  *
66  * Returns: the modified array with the element(s) or range inserted
67  */
68 T[] insertInPlace (T, RangeOrElement...) (ref T[] arr, size_t index, RangeOrElement r)
69 {
70 	stdArray.insertInPlace(arr, index, r);
71 	return arr;
72 }
73 
74 /**
75  * Removes the given elements from the given array.
76  *
77  * Params:
78  *     arr = the array to remove the elements from
79  *     elements = the elements to be removed
80  *
81  * Returns: the array with the elements removed
82  */
83 T[] remove (T) (T[] arr, T[] elements ...)
84 {
85     return algorithm.remove!((e) => elements.contains(e))(arr);
86 }
87 
88 /**
89  * Removes the elements with the given indexes from the given range.
90  *
91  * Params:
92  *     range = the range to remove the elements from
93  *     indexes = the index of the elements to be remove
94  *
95  * Returns: the range with the indexes removed
96  */
97 Range remove (Range, Index ...) (Range range, Index indexes)
98 {
99 	return algorithm.remove(range, indexes);
100 }
101 
102 /**
103  * Returns $(D_KEYWORD true) if this array contains no elements.
104  *
105  * Params:
106  *     arr = the array to check if it's empty
107  *
108  * Returns: $(D_KEYWORD true) if this array contains no elements
109  */
110 @property bool isEmpty (T) (T arr)
111     if (__traits(compiles, { auto a = arr.length; }) ||
112         __traits(compiles, { bool b = arr.empty; }))
113 {
114     static if (__traits(compiles, { auto a = arr.length; }))
115         return arr.length == 0;
116 
117     else
118         return arr.empty;
119 }
120 
121 /**
122  * Removes all of the elements from this array. The array will be empty after this call
123  * returns.
124  *
125  * Params:
126  *     arr = the array to clear
127  *
128  * Returns: the cleared array
129  *
130  * Throws: AssertException if length of the return array isn't 0
131  */
132 T[] clear (T) (ref T[] arr)
133 out (result)
134 {
135 	assert(result.length == 0, "mambo.collection.Array.clear: The length of the resulting array was not 0");
136 }
137 body
138 {
139 	arr.length = 0;
140 	return arr;
141 }
142 
143 /**
144  * Returns the index of the last occurrence of the specifed element
145  *
146  * Params:
147  *     arr = the array to get the index of the element from
148  *     element = the element to find the index of
149  *
150  * Returns: the index of the last occurrence of the element in the
151  *          specified array, or U.max
152  *          if the element does not occur.
153  *
154  * Throws: AssertException if the length of the array is 0
155  * Throws: AssertException if the return value is less than -1 or
156  * 		   greater than the length of the array - 1.
157  */
158 U lastIndexOf (T, U = size_t) (in T[] arr, T element)
159 in
160 {
161 	assert(arr.length > 0, "mambo.collection.Array.lastIndexOf: The length of the array was 0");
162 }
163 body
164 {
165 	U index = tango.text.Util.locatePrior(arr, element);
166 
167 	if (index == arr.length)
168 		return U.max;
169 
170 	return index;
171 }
172 
173 /**
174  * Returns true if a begins with b
175  *
176  * Params:
177  *     a = the array to
178  *     b =
179  *
180  * Returns: true if a begins with b, otherwise false
181  */
182 bool beginsWith (T) (T[] a, T[] b)
183 {
184 	return a.length > b.length && a[0 .. b.length] == b;
185 }
186 
187 /**
188  * Returns true if a ends with b
189  *
190  * Params:
191  *     a = the array to
192  *     b =
193  *
194  * Returns: true if a ends with b, otherwise false
195  */
196 bool endsWith (T) (T[] a, T[] b)
197 {
198 	return a.length > b.length && a[$ - b.length .. $] == b;
199 }
200 
201 /**
202  * Repests $(D_PARAM arr) $(D_PARAM number) of times.
203  *
204  * Params:
205  *     arr = the array to repeat
206  *     number = the number of times to repeat
207  *
208  * Returns: a new array containing $(D_PARAM arr) $(D_PARAM number) of times
209  */
210 T[] repeat (T) (T[] arr, size_t number)
211 {
212 	T[] result;
213 	result.reserve(arr.length * number);
214 
215 	foreach (_ ; 0 .. number)
216 		result ~= arr;
217 
218 	return result;
219 }
220 
221 /**
222  * Returns $(D_KEYWORD true) if this array contains any elements.
223  *
224  * Params:
225  *     arr = the array to check if it contains elements
226  *
227  * Returns: $(D_KEYWORD true) if this array contains elements
228  */
229 @property bool any (T) (T arr) if (__traits(compiles, { bool a = arr.isEmpty; }))
230 {
231 	return !arr.isEmpty;
232 }
233 
234 bool any (alias predicate, Range) (Range range)
235 {
236 	static if (isAssociativeArray!(Range))
237 		return _anyAA!(predicate)(range);
238 
239 	else
240 		return algorithm.any!(predicate)(range);
241 }
242 
243 /// Returns the first element of the given array.
244 @property auto first (T) (T[] arr)
245 {
246 	return stdArray.front(arr);
247 }
248 
249 /// Returns the first element of the given array.
250 @property auto last (T) (T[] arr)
251 {
252 	return stdArray.back(arr);
253 }
254 
255 /// Strips all the trailing delimiters from the given array.
256 T[] strip (T, C) (T[] arr, C delimiter)
257 {
258 	if (arr.isEmpty)
259 		return arr;
260 
261 	auto a = arr;
262 	auto del = [delimiter];
263 
264 	while (a.last == delimiter)
265 		a = stdString.chomp(a, del);
266 
267 	return a;
268 }
269 
270 auto find (alias predicate, Range) (Range range)
271 {
272 	static if (isAssociativeArray!(Range))
273 		return _findAA!(predicate)(range);
274 
275 	else
276 		return algorithm.find!(predicate)(range);
277 }
278 
279 auto reduce (alias func, Range) (Range range)
280 {
281 	return algorithm.reduce!(func)(range);
282 }
283 
284 auto reduce (alias func, Range, Seed) (Range range, Seed seed)
285 {
286 	return algorithm.reduce!(func)(seed, range);
287 }
288 
289 auto reduce (alias seed, alias func, Range) (Range range)
290 {
291 	return algorithm.reduce!(func)(seed, range);
292 }
293 
294 auto map (alias func, Range) (Range range)
295 {
296 	static if (isAssociativeArray!(Range))
297 		return _mapAA!(func)(range);
298 
299 	else
300 		return algorithm.map!(func)(range);
301 }
302 
303 inout(T)[] assumeUnique (T) (ref T[] source, ref inout(T)[] destination)
304 {
305 	destination = cast(inout(T)[]) source;
306 	source = null;
307 
308 	return destination;
309 }
310 
311 immutable(T)[] assumeUnique (T) (ref const(T)[] array)
312 {
313 	auto result = cast(immutable(T)[]) array;
314 	array = null;
315 
316 	return result;
317 }
318 
319 immutable(T)[] assumeUnique (T) (const(T)[] array)
320 {
321 	return array.assumeUnique;
322 }
323 
324 T[] toMutable (T) (const(T)[] array)
325 {
326 	return cast(T[]) array;
327 }