secretgspot
12/2/2019 - 4:20 AM

Array Methods

// https://blog.logrocket.com/understand-array-methods-by-implementing-them/

// UTIL FUNCTION
function logOperation(operationName, array, callback) {
 const input = [...array];
 const result = callback(array);

 console.log({
   operation: operationName,
   arrayBefore: input,
   arrayAfter: array,
   //mutates: mutatesArray(input, array), // shallow check
   result,
 });
}


/*
  _       __         
_|_ _  __|_  _  _ |_ 
 | (_) | |__(_|(_ | |

[1, 2, 3, 4, 5].forEach(value => console.log(value));

*/
function forEach(array, callback) {
 const { length } = array;

 for (let index = 0; index < length; index += 1) {
   const value = array[index];
   callback(value, index, array);
 }
}

logOperation('forEach', [1, 2, 3, 4, 5], array => forEach(array, value => console.log(value)));


/*
       _  _ 
   |V||_||_)
 o | || ||  

[1, 2, 3].map(number => number * 5);

*/
function map(array, callback) {
 const result = [];
 const { length } = array;

 for (let index = 0; index < length; index += 1) {
   const value = array[index];

   result[index] = callback(value, index, array);
 }

 return result;
}

logOperation('map', [1, 2, 3, 4, 5], array => map(array, value => value + 5));


/*
    _____   ___ __ _ 
   |_  | |   | |_ |_)
 o |  _|_|__ | |__| \

[1, 2, 3, 4, 5].filter(number => number >= 3);

*/
function filter(array, callback) {
 const result = [];

 const { length } = array;

 for (let index = 0; index < length; index += 1) {
   const value = array[index];

   if (callback(value, index, array)) {
     push(result, value); // 🚩 an issue, push method doesn't exist
   }
 }

 return result;
}

logOperation('filter', [1, 2, 3, 4, 5], array => filter(array, value => value >= 2));



/*
    _  __ _     __ __
   |_)|_ | \| |/  |_ 
 o | \|__|_/|_|\__|__

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].reduce((sum, number) => {
   return sum + number;
 }, 0);

*/
function reduce(array, callback, initValue) {
 const { length } = array;

 let acc = initValue;
 let startAtIndex = 0;

 if (initValue === undefined) {
   acc = array[0];
   startAtIndex = 1;
 }

 for (let index = startAtIndex; index < length; index += 1) {
   const value = array[index];
   acc = callback(acc, value, index, array);
 }

 return acc;
}

logOperation('reduce', [1, 2, 3, 4, 5], array => reduce(array, (sum, number) => sum + number, 0));






/*
     _         ___    _  __\ /
   _|_ o __  _| | |\|| \|_  X 
 o  |  | | |(_|_|_| ||_/|__/ \

[1, 2, 3, 4, 5, 6, 7].findIndex(value => value === 5); // 4

*/
function findIndex(array, callback) {
 const { length } = array;

 for (let index = 0; index < length; index += 1) {
   const value = array[index];

   if (callback(value, index, array)) {
     return index;
   }
 }

 return -1;
}

logOperation('findIndex', [1, 2, 3, 4, 5], array => findIndex(array, number => number === 3));



/*
     _
   _|_ o __  _|
 o  |  | | |(_|

[1, 2, 3, 4, 5, 6, 7].find(value => value === 5); // 4

*/
function find(array, callback) {
 const index = findIndex(array, callback);

 if (index === -1) {
   return undefined;
 }

 return array[index];
}

logOperation('find', [1, 2, 3, 4, 5], array => find(array, number => number === 3));



/*
                   _   _
    o __  _| _    / \_|_
 o  | | |(_|(/_>< \_/ | 

[3, 2, 3].indexOf(3); // -> 0

*/
function indexOf(array, searchedValue) {
 return findIndex(array, value => value === searchedValue);
}

logOperation('indexOf', [1, 2, 3, 4, 5], array => indexOf(array, 3));



/*
               ___             _   _
    |  _  _ _|_ | __  _| _    / \_|_
 o  | (_|_>  |__|_| |(_|(/_>< \_/ | 

[3, 2, 3].lastIndexOf(3); // -> 2

*/
function lastIndexOf(array, searchedValue) {
 for (let index = array.length - 1; index > -1; index -= 1) {
   const value = array[index];

   if (value === searchedValue) {
     return index;
   }
 }

 return -1;
}

logOperation('lastIndexOf', [1, 2, 3, 4, 5, 3], array => lastIndexOf(array, 3));



/*
                  
    _     _  __ \/
 o (/_\_/(/_ |  / 

[1, 2, 3].every(value => Number.isInteger(value)); // -> true

*/
function every(array, callback) {
 const { length } = array;

 for (let index = 0; index < length; index += 1) {
   const value = array[index];

   if (!callback(value, index, array)) {
     return false;
   }
 }

 return true;
}

logOperation('every', [1, 2, 3, 4, 5], array => every(array, number => Number.isInteger(number)));



/*   
    _  _ __  _ 
 o _> (_)|||(/_

[1, 2, 3, 4, 5].some(number => number === 5); // -> true

*/
function some(array, callback) {
 const { length } = array;

 for (let index = 0; index < length; index += 1) {
   const value = array[index];

   if (callback(value, index, array)) {
     return true;
   }
 }

 return false;
}

logOperation('some', [1, 2, 3, 4, 5], array => some(array, number => number === 5));



/*   
    o __  _  |     _| _  _ 
 o  | | |(_  | |_|(_|(/__> 

[1, 2, 3].includes(3); // -> true

*/
function includes(array, searchedValue) {
 return some(array, value => value === searchedValue);
}

logOperation('includes', [1, 2, 3, 4, 5], array => includes(array, 5));



/*   
     _         
   _|_ |  _ _|_
 o  |  | (_| |_

[1, 2, 3, [4, 5]].flat(1) // -> [1, 2, 3, 4, 5]

*/
function flat(array, depth = 0) {
 if (depth < 1 || !Array.isArray(array)) {
   return array;
 }

 return reduce( // 🚩
   array,
   (result, current) => {
     return concat(result, flat(current, depth - 1));
   },
   [],
 );
}

logOperation('flat', [1, 2, 3, [4, 5, [6]]], array => flat(array, 2));



/*   
     _                _ 
   _|_ |  _ _|_|V| _ |_)
 o  |  | (_| |_| |(_||  

[1, 2, 3].flatMap(value => [value, value, value]); // [1, 1, 1, 2, 2, 2, 3, 3, 3]

*/
function flatMap(array, callback) {
 return flat(map(array, callback), 1);
}

logOperation('flatMap', [1, 2, 3], array => flatMap(array, number => [number, number]));



/*   
                     
    _  _ __  _  _ _|_
 o (_ (_)| |(_ (_| |_

[1, 2, 3].concat([4, 5], 6, [7, 8]) // -> [1, 2, 3, 4, 5, 6, 7, 8]

*/
function concat(array, ...values) {
 const result = [...array];
 const { length } = values;

 for (let index = 0; index < length; index += 1) {
   const value = values[index];

   if (Array.isArray(value)) {
     push(result, ...value);
   } else {
     push(result, value);
   }
 }

 return result;
}

logOperation('concat', [1, 2, 3, 4, 5], array => concat(array, 1, 2, [3, 4]));



/*   
    o          
    |  _  o __ 
 o _| (_) | | |

['Brian', 'Matt', 'Kate'].join(', ') // -> Brian, Matt, Kate

*/
function join(array, joinWith) {
 return reduce(
   array,
   (result, current, index) => {
     if (index === 0) {
       return current;
     }

     return `${result}${joinWith}${current}`;
   },
   '',
 );
}

logOperation('join', [1, 2, 3, 4, 5], array => join(array, ', '));



/*   
    __ _     _  __ _  _ 
 o  | (/_\_/(/_ | _> (/_

[1, 2, 3].reverse(); // -> [3, 2, 1]

*/
function reverse(array) {
 const result = [];

 const lastIndex = array.length - 1;

 for (let index = lastIndex; index > -1; index -= 1) {
   const value = array[index];
   result[lastIndex - index] = value;
 }

 return result;
}

logOperation('reverse', [1, 2, 3, 4, 5], array => reverse(array));



/*   
              _   
    _ |_  o _|__|_
 o _> | | |  |  |_

[1, 2, 3].shift(); // -> 1

*/
function shift(array) {
 const { length } = array;
 const firstValue = array[0];

 for (let index = 1; index < length; index += 1) {
   const value = array[index];
   array[index - 1] = value;
 }

 array.length = length - 1;

 return firstValue;
}

logOperation('shift', [1, 2, 3, 4, 5], array => shift(array));



/*   
                    _   
      __  _ |_  o _|__|_
 o |_|| |_> | | |  |  |_

[2, 3, 4].unshift(1); // -> [1, 2, 3, 4]

*/
function unshift(array, ...values) {
 const mergedArrays = concat(values, ...array);
 const { length: mergedArraysLength } = mergedArrays;

 for (let index = 0; index < mergedArraysLength; index += 1) {
   const value = mergedArrays[index];
   array[index] = value;
 }

 return array.length;
}

logOperation('unshift', [1, 2, 3, 4, 5], array => unshift(array, 0));



/*   
                  
    _  |  o  _  _ 
 o _>  |  | (_ (/_

[1, 2, 3, 4, 5, 6, 7].slice(3, 6); // -> [4, 5, 6]

*/
function slice(array, startIndex = 0, endIndex = array.length) {
 const result = [];

 for (let index = startIndex; index < endIndex; index += 1) {
   const value = array[index];

   if (index < array.length) {
     push(result, value);
   }
 }

 return result;
}

logOperation('slice', [1, 2, 3, 4, 5], array => slice(array, 1, 3));



/*   
       _             
    _ |_) |  o  _  _ 
 o _> |   |  | (_ (/_

const arr = [1, 2, 3, 4, 5];
arr.splice(0, 2, 3, 4, 5);
arr // -> [3, 4, 5, 3, 4, 5]

*/
function splice<T>(array: T[], insertAtIndex: number, removeNumberOfElements: number, ...values: T[]) {
  const firstPart = slice(array, 0, insertAtIndex);
  const secondPart = slice(array, insertAtIndex + removeNumberOfElements);

  const removedElements = slice(array, insertAtIndex, insertAtIndex + removeNumberOfElements);

  const joinedParts = firstPart.concat(values, secondPart);
  const { length: joinedPartsLength } = joinedParts;

  for (let index = 0; index < joinedPartsLength; index += 1) {
    array[index] = joinedParts[index];
  }

  array.length = joinedPartsLength;

  return removedElements;
}

logOperation('splice', [1, 2, 3, 4, 5], array => splice(array, 1, 3));



/*   
    _     _ 
   |_) _ |_)
 o |  (_)|  

[1, 2, 3].pop(); // -> 3

*/
function pop(array) {
 const value = array[array.length - 1];

 array.length = array.length - 1;

 return value;
}

logOperation('pop', [1, 2, 3, 4, 5], array => pop(array));



/*   
    _          
   |_)    _ |_ 
 o |  |_|_> | |

[1, 2, 3, 4].push(5); // -> [1, 2, 3, 4, 5]

*/
export function push(array, ...values) {
 const { length: arrayLength } = array;
 const { length: valuesLength } = values;

 for (let index = 0; index < valuesLength; index += 1) {
   array[arrayLength + index] = values[index];
 }

 return array.length;
}

logOperation('push', [1, 2, 3, 4, 5], array => push(array, 6, 7));



/*   
     _         
   _|_ o  |  | 
 o  |  |  |  | 

[...Array(5)].fill(null) // -> [null, null, null, null, null]

*/
function fill(array, value, startIndex = 0, endIndex = array.length) {
 for (let index = startIndex; index < endIndex; index += 1) {
   array[index] = value;
 }

 return array;
}

logOperation('fill', [...new Array(5)], array => fill(array, 0));




/*   
                     
       _  |     _  _ 
 o \_/(_| | |_|(/__> 

const valuesGenerator = values([1, 2, 3, 4, 5]);
valuesGenerator.next(); // { value: 1, done: false }

*/
function values(array) {
 const { length } = array;

 function* createGenerator() {
   for (let index = 0; index < length; index += 1) {
     const value = array[index];
     yield value;
   }
 }

 return createGenerator();
}




/*   
               
    |  _  \/ _ 
 o  |<(/_ / _> 

const keysGenerator = keys([1, 2, 3, 4, 5]);
keysGenerator.next(); // { value: 0, done: false }

*/
function keys(array) {
 function* createGenerator() {
   const { length } = array;

   for (let index = 0; index < length; index += 1) {
     yield index;
   }
 }

 return createGenerator();
}




/*   
    _ __ _|_ __ o  _  _ 
 o (/_| | |_ |  | (/__> 

const entriesGenerator = entries([1, 2, 3, 4, 5]);
entriesGenerator.next(); // { value: [0, 1], done: false }

*/
function entries(array) {
 const { length } = array;

 function* createGenerator() {
   for (let index = 0; index < length; index += 1) {
     const value = array[index];
     yield [index, value];
   }
 }

 return createGenerator();
}