-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathunderbar.js
More file actions
418 lines (369 loc) · 12.8 KB
/
Copy pathunderbar.js
File metadata and controls
418 lines (369 loc) · 12.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
(function() {
'use strict';
window._ = {};
// Returns whatever value is passed as the argument. This function doesn't
// seem very useful, but remember it--if a function needs to provide an
// iterator when the user does not pass one in, this will be handy.
_.identity = function(val) {
};
/**
* COLLECTIONS
* ===========
*
* In this section, we'll have a look at functions that operate on collections
* of values; in JavaScript, a 'collection' is something that can contain a
* number of values--either an array or an object.
*
*
* IMPORTANT NOTE!
* ===========
*
* The .first function is implemented for you, to help guide you toward success
* in your work on the following functions. Whenever you see a portion of the
* assignment pre-completed, be sure to read and understand it fully before
* you proceed. Skipping this step will lead to considerably more difficulty
* implementing the sections you are responsible for.
*/
// Return an array of the first n elements of an array. If n is undefined,
// return just the first element.
_.first = function(array, n) {
return n === undefined ? array[0] : array.slice(0, n);
};
// Like first, but for the last elements. If n is undefined, return just the
// last element.
_.last = function(array, n) {
if(n > array.length)
{
return array;
}
return n === undefined ? array[array.length - 1] : array.slice(array.length -1, array.length)
};
// Call iterator(value, key, collection) for each element of collection.
// Accepts both arrays and objects.
//
// Note: _.each does not have a return value, but rather simply runs the
// iterator function over each item in the input collection.
_.each = function(collection, iterator) {
if(Array.isArray(collection))
{
for(var i = 0; i < collection.length; i++)
{
iterator(cllection[i], i, collection);
}
}
else if(typeof collection === 'object')
{
for(var key in collection)
{
iterator(collection[key], key, collection);
}
}
};
// Returns the index at which value can be found in the array, or -1 if value
// is not present in the array.
_.indexOf = function(array, target){
// TIP: Here's an example of a function that needs to iterate, which we've
// implemented for you. Instead of using a standard `for` loop, though,
// it uses the iteration helper `each`, which you will need to write.
var result = -1;
_.each(array, function(item, index) {
if (item === target && result === -1) {
result = index;
}
});
return result;
};
// Return all elements of an array that pass a truth test.
_.filter = function(collection, test) {
var elements = [];
_.each(collection, function(item) {
if (test(item)) {
elements.push(item);
}
});
return elements;
};
// Return all elements of an array that don't pass a truth test.
_.reject = function(collection, test) {
// TIP: see if you can re-use _.filter() here, without simply
// copying code in and modifying it
return _.filter(collection, function(item))
{
retur !test(item);
});
};
// Produce a duplicate-free version of the array.
_.uniq = function(array, isSorted, iterator) {
var unique = [];
var hash = {};
iterator = isSorted && iterator) || _.identity;
_.each(array, function(item)
{
var change = iterator(item);
if(hash[change] === undefined)
{
hash[change] = item;
}
});
_.each(hash, function(value){
unique.push(value);
});
return unique;
};
// Return the results of applying an iterator to each element.
_.map = function(collection, iterator) {
// map() is a useful primitive iteration function that works a lot
// like each(), but in addition to running the operation on all
// the members, it also maintains an array of results.
var answer = [];
_.each(collection, function(item){
answer.push(iterator(item));
});
return answer;
};
/*
* TIP: map is really handy when you want to transform an array of
* values into a new array of values. _.pluck() is solved for you
* as an example of this.
*/
// Takes an array of objects and returns and array of the values of
// a certain property in it. E.g. take an array of people and return
// an array of just their ages
_.pluck = function(collection, key) {
// TIP: map is really handy when you want to transform an array of
// values into a new array of values. _.pluck() is solved for you
// as an example of this.
return _.map(collection, function(item){
return item[key];
});
};
// Reduces an array or object to a single value by repetitively calling
// iterator(accumulator, item) for each item. accumulator should be
// the return value of the previous iterator call.
//
// You can pass in a starting value for the accumulator as the third argument
// to reduce. If no starting value is passed, the first element is used as
// the accumulator, and is never passed to the iterator. In other words, in
// the case where a starting value is not passed, the iterator is not invoked
// until the second element, with the first element as its second argument.
//
// Example:
// var numbers = [1,2,3];
// var sum = _.reduce(numbers, function(total, number){
// return total + number;
// }, 0); // should be 6
//
// var identity = _.reduce([5], function(total, number){
// return total + number * number;
// }); // should be 5, regardless of the iterator function passed in
// No accumulator is given so the first element is used.
_.reduce = function(collection, iterator, accumulator) {
var noAcc == arguments.length === 2;
//go through collection
_.each(collection, function(item)
{
if(noAcc)
{
noAcc = false;
accumulator = item;
}
else
{
accumulator = iterator(accumulator, item);
}
});
return accumulator;
};
// Determine if the array or object contains a given value (using `===`).
_.contains = function(collection, target) {
// TIP: Many iteration problems can be most easily expressed in
// terms of reduce(). Here's a freebie to demonstrate!
return _.reduce(collection, function(wasFound, item) {
if (wasFound) {
return true;
}
return item === target;
}, false);
};
// Determine whether all of the elements match a truth test.
_.every = function(collection, iterator) {
// TIP: Try re-using reduce() here.
return _.reduce(collection, function(isTrue, item)
{
return isTrue && Boolean(iterator(item));
}, true);
};
// Determine whether any of the elements pass a truth test. If no iterator is
// provided, provide a default one
_.some = function(collection, iterator) {
// TIP: There's a very clever way to re-use every() here.
iterator = iterator || _.identity;
return !_.every(collection, function(item)
{
return !iterator(item);
});
};
/**
* OBJECTS
* =======
*
* In this section, we'll look at a couple of helpers for merging objects.
*/
// Extend a given object with all the properties of the passed in
// object(s).
//
// Example:
// var obj1 = {key1: "something"};
// _.extend(obj1, {
// key2: "something new",
// key3: "something else new"
// }, {
// bla: "even more stuff"
// }); // obj1 now contains key1, key2, key3 and bla
_.extend = function(obj) {
var input = Array.prototype.slice.call(arguments, 1);
_.each(input, function(object) {
_.each(object, function(prop, key) {
obj[key] = prop;
});
});
return obj;
};
};
// Like extend, but doesn't ever overwrite a key that already
// exists in obj
_.defaults = function(obj) {
var input = Array.prototype.slice.call(arguments, 1);
_.each(input, function(object) {
_.each(object, function(prop, key) {
if (!obj.hasOwnProperty(key)) {
obj[key] = prop;
}
});
});
return obj;
};
/**
* FUNCTIONS
* =========
*
* Now we're getting into function decorators, which take in any function
* and return out a new version of the function that works somewhat differently
*/
// Return a function that can be called at most one time. Subsequent calls
// should return the previously returned value.
_.once = function(func) {
// TIP: These variables are stored in a "closure scope" (worth researching),
// so that they'll remain available to the newly-generated function every
// time it's called.
var alreadyCalled = false;
var result;
// TIP: We'll return a new function that delegates to the old one, but only
// if it hasn't been called before.
return function() {
if (!alreadyCalled) {
// TIP: .apply(this, arguments) is the standard way to pass on all of the
// infromation from one function call to another.
result = func.apply(this, arguments);
alreadyCalled = true;
}
// The new function always returns the originally computed result.
return result;
};
};
// Memorize an expensive function's results by storing them. You may assume
// that the function only takes primitives as arguments.
// memoize could be renamed to oncePerUniqueArgumentList; memoize does the
// same thing as once, but based on many sets of unique arguments.
//
// _.memoize should return a function that, when called, will check if it has
// already computed the result for the given argument and return that value
// instead if possible.
_.memoize = function(func) {
let info = {};
return function(){
let serial = JSON.stringify(arguments);
return info[serial] = info[serial] || func.apply(this.arguments);
}
};
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
//
// The arguments for the original function are passed after the wait
// parameter. For example _.delay(someFunction, 500, 'a', 'b') will
// call someFunction('a', 'b') after 500ms
_.delay = function(func, wait) {
var info = Array.prototype.slice.call(arguments, 2);
setTimeout(function() {
return func.apply(null, info);
}, wait);
};
/**
* ADVANCED COLLECTION OPERATIONS
* ==============================
*/
// Randomizes the order of an array's contents.
//
// TIP: This function's test suite will ask that you not modify the original
// input array. For a tip on how to make a copy of an array, see:
// http://mdn.io/Array.prototype.slice
_.shuffle = function(array) {
var isSorted = array.slice();
var remainder = isSorted.length;
var temp;
var curr;
while(remainder)
{
curr = Math.floor(Math.random()*remainder--);
temp = isSorted[remainder];
isSorted[remainder] = isSorted[curr];
isSorted[curr] = temp;
}
return isSorted;
};
/**
* ADVANCED
* =================
*
* Note: This is the end of the pre-course curriculum. Feel free to continue,
* but nothing beyond here is required.
*/
// Calls the method named by functionOrKey on each value in the list.
// Note: You will need to learn a bit about .apply to complete this.
_.invoke = function(collection, functionOrKey, args) {
};
// Sort the object's values by a criterion produced by an iterator.
// If iterator is a string, sort objects by that property with the name
// of that string. For example, _.sortBy(people, 'name') should sort
// an array of people by their name.
_.sortBy = function(collection, iterator) {
};
// Zip together two or more arrays with elements of the same index
// going together.
//
// Example:
// _.zip(['a','b','c','d'], [1,2,3]) returns [['a',1], ['b',2], ['c',3], ['d',undefined]]
_.zip = function() {
};
// Takes a multidimensional array and converts it to a one-dimensional array.
// The new array should contain all elements of the multidimensional array.
//
// Hint: Use Array.isArray to check if something is an array
_.flatten = function(nestedArray, result) {
};
// Takes an arbitrary number of arrays and produces an array that contains
// every item shared between all the passed-in arrays.
_.intersection = function() {
};
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_.difference = function(array) {
};
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. See the Underbar readme for extra details
// on this function.
//
// Note: This is difficult! It may take a while to implement.
_.throttle = function(func, wait) {
};
}());