diff --git a/src/refactoring/areumsheep/answers/1.js b/src/refactoring/areumsheep/answers/1.js new file mode 100644 index 0000000..c4d12ba --- /dev/null +++ b/src/refactoring/areumsheep/answers/1.js @@ -0,0 +1,33 @@ +// 랜덤한 숫자 배열에서 짝수 위치는 더하고, 홀수 위치는 곱한 후에 순서대로 누적해라 + +const { pipe } = require('../utils/pipe'); + +const 짝수_더하기_홀수_곱하기 = (array) => { + return array.reduce((acc, cur, idx, arr) => { + if (idx % 2 === 0) acc.push(cur + (arr[idx - 2] ?? 0)); + else acc.push(cur * (arr[idx - 2] ?? 1)); + return acc; + }, []); +}; + +const 배수_분리하기 = (array) => { + const t = []; + const h = []; + + array.map((value) => { + if (value % 2 === 0) { + t.push(value); + } + if (value % 3 === 0) { + h.push(value); + } + }); + return { + t: t, + h: h, + }; +}; + +const call = pipe(짝수_더하기_홀수_곱하기, 배수_분리하기); +console.log(call([1, 2, 3, 4, 5, 6, 7])); + diff --git a/src/refactoring/areumsheep/answers/2.js b/src/refactoring/areumsheep/answers/2.js new file mode 100644 index 0000000..3229a35 --- /dev/null +++ b/src/refactoring/areumsheep/answers/2.js @@ -0,0 +1,16 @@ +// 전, 후 사이 공백이 존재하는 어떤 문자열을 입력받을 때, +// 쉼표 기준으로 문자열을 자르고 +// 전, 후에 존재하는 공백은 제거하고 +// 모든 문자열을 비교하고 중복된 값을 제거하고 +// 문자열의 길이 순서대로 반환해주세요. + +const { pipe } = require('../utils/pipe'); +const { reduce } = require('../utils/reduce'); + +const splitByComma = (str) => str.split(','); +const trimArray = (array) => reduce(array, (acc, value) => acc.concat(value.trim()), []); +const distinctArray = (array) => [...new Set(array)]; +const compareLength = (array) => array.sort((a, b) => a.length - b.length); + +const call = pipe(splitByComma, trimArray, distinctArray, compareLength); +console.log(call('asfasdfkl , sdf,asdf as , ddsf,asdfnkl, as, as')); \ No newline at end of file diff --git a/src/refactoring/areumsheep/example-1.js b/src/refactoring/areumsheep/example-1.js new file mode 100644 index 0000000..4faf69c --- /dev/null +++ b/src/refactoring/areumsheep/example-1.js @@ -0,0 +1,19 @@ +// 같은 형태의 함수가 반복되는 것으로 보여 field 라는 값을 받아 공통으로 작업하는 함수를 만들었습니다. + +const setFieldByName = (cart, name, field, value) => { + const item = cart[name]; + const newItem = objectSet(item, field, value); + const newCart = objectSet(cart, name, newItem); + return newCart; +} + +const setPriceByName = (cart, name, price) => setFieldByName(cart, name, 'price', price) +const setShippingByName = (cart, name, ship) => setFieldByName(cart, name, 'shipping', ship); +const setQuantityByName = (cart, name, quant) => setFieldByName(cart, name, 'quantity', quant); +const setTaxByName = (cart, name, tax) => setFieldByName(cart, name, 'tax', tax); + +function objectSet(object, key, value) { + var copy = Object.assign({}, object); + copy[key] = value; + return copy; +} diff --git a/src/refactoring/areumsheep/example-2.js b/src/refactoring/areumsheep/example-2.js new file mode 100644 index 0000000..7f34645 --- /dev/null +++ b/src/refactoring/areumsheep/example-2.js @@ -0,0 +1,19 @@ +// for (var i = 0; i < items.length; i++) { } << 형태가 반복된다....?? + + +const forEach = (items, logic) => { + for (let i = 0; i < items.length; i++) { + logic(items[i]); + } +} + +forEach(foods, (food) => { + cook(food); + eat(food); +}); + +forEach(dishes, (dish) => { + wash(dish); + dry(dish); + putAway(dish); +}); \ No newline at end of file diff --git a/src/refactoring/areumsheep/example-3.js b/src/refactoring/areumsheep/example-3.js new file mode 100644 index 0000000..2d777c3 --- /dev/null +++ b/src/refactoring/areumsheep/example-3.js @@ -0,0 +1,21 @@ +// try-catch가 과연 여기에 있어야하나??? +// error는 어떻게 핸들링하면 좋은가? + +var user = { + id: '1', +}; + +const catchError = (logic) => { + try { + logic(); + } catch (error) { + console.log(`🚫 에러가 발생했어요: ${error.message}`) + } +} + +const getUserData = async ({ id }) => { + const response = await fetch(`https://jsonplaceholder.typicode.com/users?id=${id}`); + return response.json(); +} + +catchError(getUserData); \ No newline at end of file diff --git a/src/refactoring/areumsheep/internal_utils.js b/src/refactoring/areumsheep/internal_utils.js new file mode 100644 index 0000000..db32eea --- /dev/null +++ b/src/refactoring/areumsheep/internal_utils.js @@ -0,0 +1,5 @@ +const isIterable = (value) => typeof value[Symbol.iterator] === "function"; + +exports.I = { + isIterable +}; \ No newline at end of file diff --git a/src/refactoring/areumsheep/promise.js b/src/refactoring/areumsheep/promise.js new file mode 100644 index 0000000..08a3b2c --- /dev/null +++ b/src/refactoring/areumsheep/promise.js @@ -0,0 +1,57 @@ +const PROMISES_STATE = Object.freeze({ + pending: 'pending', + fulfilled: 'fulfilled', + rejected: 'rejected', +}); + +class MyPromise { + #state = PROMISES_STATE.pending; + #value; + #resolveLastcalls = []; + #rejectLastcalls = []; + + constructor(fn) { + try { + fn(this.#resolve.bind(this), this.#reject.bind(this)); + } catch (e) { + this.#reject(e); + } + } + + #resolve(value) { + queueMicrotask(() => { + this.#state = PROMISES_STATE.fulfilled; + this.#value = value; + this.#resolveLastcalls.forEach(call => call(this.#state)); + }); + } + #reject(error) { + queueMicrotask(() => { + this.#state = PROMISES_STATE.rejected; + this.#value = error; + this.#rejectLastcalls.forEach(call => call(this.#state)); + }); + } + + then(callback) { + if (this.#state === PROMISES_STATE.pending) { + return new MyPromise(resolve => { + this.#resolveLastcalls.push(() => resolve(callback(this.#value))); + }); + } else if (this.#state === PROMISES_STATE.fulfilled) { + return new MyPromise(resolve => resolve(callback(this.#value))); + } + } + catch(callback) { + if (this.#state === PROMISES_STATE.rejected) { + callback(this.#value); + } + return this; + } + finally(callback) { + return this.then((value) => { + callback(); + return value; + }) + } +} \ No newline at end of file diff --git a/src/refactoring/areumsheep/utils/generator_utils.js b/src/refactoring/areumsheep/utils/generator_utils.js new file mode 100644 index 0000000..08827e2 --- /dev/null +++ b/src/refactoring/areumsheep/utils/generator_utils.js @@ -0,0 +1,23 @@ +const { timer, getMockData } = require('./lib'); +const { L } = require('./lazy/_index'); + +const users = getMockData(); + +timer.start(); +const job = L.pipe( + L.map(user => user.age), + L.filter(age => age > 50), +); +const test = job(users); +timer.end(); + +console.log(test.next()); +console.log(test.next()); +console.log(test.next()); + +timer.start(); +users + .map(user => user.age) + .filter(age => age > 50) + .slice(2); +timer.end(); diff --git a/src/refactoring/areumsheep/utils/lazy/_index.js b/src/refactoring/areumsheep/utils/lazy/_index.js new file mode 100644 index 0000000..c95b669 --- /dev/null +++ b/src/refactoring/areumsheep/utils/lazy/_index.js @@ -0,0 +1,15 @@ +const { filter } = require('./filter'); +const { groupBy } = require('./groupBy'); +const { map } = require('./map'); +const { pipe } = require('./pipe'); +const { reduce } = require('./reduce'); +const { uniqueBy } = require('./uniqueBy'); + +exports.L = { + filter, + groupBy, + map, + pipe, + reduce, + uniqueBy, +}; diff --git a/src/refactoring/areumsheep/utils/lazy/filter.js b/src/refactoring/areumsheep/utils/lazy/filter.js new file mode 100644 index 0000000..82da499 --- /dev/null +++ b/src/refactoring/areumsheep/utils/lazy/filter.js @@ -0,0 +1,9 @@ +function filter(fn) { + return function* (iter) { + for (const item of iter) { + if (fn(item)) yield item; + } + }; +} + +exports.filter = filter; diff --git a/src/refactoring/areumsheep/utils/lazy/groupBy.js b/src/refactoring/areumsheep/utils/lazy/groupBy.js new file mode 100644 index 0000000..3e3a88a --- /dev/null +++ b/src/refactoring/areumsheep/utils/lazy/groupBy.js @@ -0,0 +1,21 @@ +const { I } = require('../../internal_utils'); +const { NL } = require('../non-lazy/_index'); + +const groupBy = (condition) => { + return (iter) => { + if (!I.isIterable(iter)) { + iter = Object.values(iter); + } + return NL.reduce(iter, (acc, value) => { + let key = value[condition]; + if (typeof condition === 'function') { + key = condition(value); + } + acc[key] = acc[key] || []; + acc[key].push(value); + return acc; + }, {}); + }; +}; + +exports.groupBy = groupBy; \ No newline at end of file diff --git a/src/refactoring/areumsheep/utils/lazy/map.js b/src/refactoring/areumsheep/utils/lazy/map.js new file mode 100644 index 0000000..dd8cb19 --- /dev/null +++ b/src/refactoring/areumsheep/utils/lazy/map.js @@ -0,0 +1,9 @@ +function map(fn) { + return function* (iter) { + for (const item of iter) { + yield fn(item); + } + }; +} + +exports.map = map; diff --git a/src/refactoring/areumsheep/utils/lazy/pipe.js b/src/refactoring/areumsheep/utils/lazy/pipe.js new file mode 100644 index 0000000..fb57459 --- /dev/null +++ b/src/refactoring/areumsheep/utils/lazy/pipe.js @@ -0,0 +1,8 @@ +const { NL } = require('../non-lazy/_index'); + +const pipe = + (...logics) => + target => + NL.reduce(logics, (value, logic) => logic(value), target); + +exports.pipe = pipe; diff --git a/src/refactoring/areumsheep/utils/lazy/reduce.js b/src/refactoring/areumsheep/utils/lazy/reduce.js new file mode 100644 index 0000000..47b36e4 --- /dev/null +++ b/src/refactoring/areumsheep/utils/lazy/reduce.js @@ -0,0 +1,22 @@ +function reduce(fn) { + return (array, initialValue) => { + let i = -1; + let returnValue = initialValue; + const arrayLength = array.length; + + if (initialValue === undefined && arrayLength >= 1) { + i++; + returnValue = array[0]; + } + if (returnValue === undefined) { + throw new TypeError('Reduce of empty array with no initial value'); + } + + while (++i < arrayLength) { + returnValue = fn(returnValue, array[i], i, ...array); + } + return returnValue; + } +} + +exports.reduce = reduce; \ No newline at end of file diff --git a/src/refactoring/areumsheep/utils/lazy/test/groupBy.test.js b/src/refactoring/areumsheep/utils/lazy/test/groupBy.test.js new file mode 100644 index 0000000..5e4a8c4 --- /dev/null +++ b/src/refactoring/areumsheep/utils/lazy/test/groupBy.test.js @@ -0,0 +1,13 @@ +const { L } = require('../_index'); +const { NL } = require('../../non-lazy/_index'); + +describe('groupBy 테스트', () => { + describe('lazy', () => { + it('case: 1, Advanced', () => { + const decimalNumbers = [2.3, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 3.2, 2.6, 2.8]; + const groupByFloor = L.groupBy(Math.floor); + + expect(groupByFloor(decimalNumbers)).toEqual(NL.groupBy(decimalNumbers, Math.floor)); + }); + }); +}); diff --git a/src/refactoring/areumsheep/utils/lazy/test/reduce.test.js b/src/refactoring/areumsheep/utils/lazy/test/reduce.test.js new file mode 100644 index 0000000..2661eda --- /dev/null +++ b/src/refactoring/areumsheep/utils/lazy/test/reduce.test.js @@ -0,0 +1,14 @@ +const { L } = require('../_index'); +const { NL } = require('../../non-lazy/_index'); + +describe('reduce 테스트', () => { + describe('lazy', () => { + it('case: 1, Advanced', () => { + const decimalNumbers = [2.3, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 3.2, 2.6, 2.8]; + const addSum = L.reduce((acc, value) => acc + value); + + expect(addSum(decimalNumbers)).toEqual(NL.reduce(decimalNumbers, (acc, value) => acc + value)); + }); + + }); +}); diff --git a/src/refactoring/areumsheep/utils/lazy/test/uniqueBy.test.js b/src/refactoring/areumsheep/utils/lazy/test/uniqueBy.test.js new file mode 100644 index 0000000..0c5da18 --- /dev/null +++ b/src/refactoring/areumsheep/utils/lazy/test/uniqueBy.test.js @@ -0,0 +1,13 @@ +const { L } = require('../_index'); +const { NL } = require('../../non-lazy/_index'); + +describe('uniqueBy 테스트', () => { + describe('lazy', () => { + it('case: 1, Advanced', () => { + const decimalNumbers = [2.3, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 3.2, 2.6, 2.8]; + const uniqueByFloor = L.uniqueBy(Math.floor); + + expect(uniqueByFloor(decimalNumbers)).toEqual(NL.uniqueBy(decimalNumbers, Math.floor)); + }); + }); +}); diff --git a/src/refactoring/areumsheep/utils/lazy/uniqueBy.js b/src/refactoring/areumsheep/utils/lazy/uniqueBy.js new file mode 100644 index 0000000..b014da7 --- /dev/null +++ b/src/refactoring/areumsheep/utils/lazy/uniqueBy.js @@ -0,0 +1,35 @@ +const { I } = require('../../internal_utils'); +const { NL } = require('../non-lazy/_index'); + +const uniqueBy = callback => { + const map = new Map(); + return iter => { + if (!I.isIterable(iter)) { + return []; + } + return NL.reduce( + iter, + (acc, item) => { + let key; + // Object인 경우 + if (!Array.isArray(item)) { + key = JSON.stringify(item); + } + // callback이 있는 경우 + if (typeof callback === 'function') { + key = callback(item); + } + + if (!map.has(key)) { + acc.push(item); + } + map.set(key, item); + + return acc; + }, + [], + ); + }; +}; + +exports.uniqueBy = uniqueBy; diff --git a/src/refactoring/areumsheep/utils/lib.js b/src/refactoring/areumsheep/utils/lib.js new file mode 100644 index 0000000..f1f348c --- /dev/null +++ b/src/refactoring/areumsheep/utils/lib.js @@ -0,0 +1,375 @@ +// FIXME: 프로토타입 ================================================================= +Array.prototype.generate = function (size = 10) { + let returnArray = []; + + while (returnArray.length < size) { + const temp = this[parseInt(Math.random() * this.length)]; + + returnArray = [...returnArray, temp]; + } + + return returnArray; +}; + +Array.prototype.shuffle = function () { + for (let i = this.length - 1; i > 0; i--) { + let j = Math.floor(Math.random() * (i + 1)); + [this[i], this[j]] = [this[j], this[i]]; + } + return this; +}; + +// FIXME: 상수 라인=================================================================== +const 엄청_큰_배열_크기 = 10000; + +const 이름_목록 = [ + "Zachary", + "Zayden", + "Zane", + "Zion", + "Yadiel", + "Yousef", + "Xavier", + "Xander", + "Xayden", + "William", + "Wyatt", + "Wesley", + "Walter", + "Wade", + "Warren", + "Winston", + "Willie", + "Will", + "Woodrow", + "VVincent", + "Valentino", + "Victor", + "Vincenzo", + "Van", + "Vivaan", + "Vihaan", + "Vance", + "Ucal", + "Ulan", + "Uluka", + "Uner", + "Uriah", + "Thomas", + "Tyler", + "Theodore", + "Tony", + "Travis", + "Troy", + "Timothy", + "Titan", + "Toby", + "Tanner", + "Samuel", + "Steven", + "Sebastian", + "Santiago", + "Simon", + "Sean", + "Stephen", + "Scott", + "Ryan", + "Randolph", + "Robert", + "Roman", + "River", + "Raymond", + "Richard", + "Rowan", + "Russell", + "Ryder", + "Quincy", + "Quade", + "Parker", + "Patrick", + "Paul", + "Peter", + "Prince", + "Phillip", + "Pablo", + "Pedro", + "Oliver", + "Owen", + "Oscar", + "Orlando", + "Odin", + "Oakley", + "Ozzy", + "Ollie", + "Noah", + "Nicholas", + "Nolan", + "Nikolai", + "Nathan", + "Nelson", + "Matthew", + "Maddox", + "Max", + "Michael", + "Mason", + "Marcus", + "Miles", + "Mark", + "Mario", + "Mandy", + "Liam", + "Logan", + "Lucas", + "Luke", + "Lincoln", + "Leo", + "Luis", + "Leonardo", + "Luciano", + "Lucian", + "Kevin", + "Kai", + "Kale", + "Kade", + "Kaiden", + "Knox", + "Ken", + "Kendrick", + "Jayden", + "James", + "Jacob", + "Julian", + "Jackson", + "Jeffery", + "Jarry", + "Jeremy", + "Jayce", + "Jonathan", + "Justin", + "Joel", + "Ian", + "Ivan", + "Ismael", + "Israel", + "Isaac", + "Iker", + "Henry", + "Hunter", + "Hayden", + "Hendrix", + "Harry", + "Hugo", + "Harrison", + "Harvey", + "Hudson", + "Gabriel", + "Gael", + "Gunner", + "Garrett", + "Gregory", + "Griffin", + "Grady", + "George", + "Francisco", + "Felix", + "Fernando", + "Fabian", + "Finn", + "Frankie", + "Fredrick", + "Fabbro", + "Floyd", + "Ethan", + "Eli", + "Evan", + "Eric", + "Emmett", + "Elliott", + "Everett", + "Enzo", + "Danial", + "Dylan", + "Damian", + "Declan", + "Derek", + "Dante", + "Dominick", + "Dilan", + "Drew", + "Chistopher", + "Connor", + "Charles", + "Caleb", + "Cooper", + "Colin", + "Corbin", + "Covy", + "Chico", + "Benjamin", + "Brandon", + "Bentley", + "Blake", + "Brody", + "Bennett", + "Bradley", + "Brooks", + "Bruno", + "Aiden", + "Andew", + "Adrian", + "Asher", + "Austin", + "Ayaan", + "Aaron", + "Alex", + "Andres", + "Antonio", + "Archer", +]; + +const 도시_목록 = [ + "Afghanistan", + "Algeria", + "Angola", + "Argentina", + "Austria", + "Australia", + "Bangladesh", + "Belarus", + "Belgium", + "Bolivia", + "Bosnia", + "Brazil", + "Britain", + "Bulgaria", + "Cambodia", + "Cameroon", + "Canada", + "Central", + "Chad", + "China", + "Colombia", + "Costa", + "Croatia", + "the", + "Democratic", + "Denmark", + "Ecuador", + "Egypt", + "El", + "England", + "Estonia", + "Ethiopia", + "Finland", + "France", + "Germany", + "Ghana", + "Greece", + "Guatemala", + "Holland", + "Honduras", + "Hungary", + "Iceland", + "India", + "Indonesia", + "Iran", + "Iraq", + "Ireland", + "Israel", + "Italy", + "Ivory", + "Jamaica", + "Japan", + "Jordan", + "Kazakhstan", + "Kenya", + "Laos", + "Latvia", + "Libya", + "Lithuania", + "Madagascar", + "Malaysia", + "Mali", + "Mauritania", + "Mexico", + "Morocco", + "Namibia", + "New", + "Nicaragua", + "Niger", + "Nigeria", + "Norway", + "Oman", + "Pakistan", + "Panama", + "Paraguay", + "Peru", + "The", + "Poland", + "Portugal", + "Republic", + "Romania", + "Russia", + "Saudi", + "Scotland", + "Senegal", + "Serbia", + "Singapore", + "Slovakia", + "Somalia", + "South", + "Spain", + "Sudan", + "Sweden", + "Switzerland", + "Syria", + "Thailand", + "Tunisia", + "Turkey", + "Turkmenistan", + "Ukraine", + "The", + "The", + "Uruguay", + "Vietnam", + "Wales", + "Zambia", + "Zimbabwe", +]; + +const 무작위_이름_목록 = 이름_목록.generate(엄청_큰_배열_크기); +const 무작위_나이_목록 = Array.from( + { length: 엄청_큰_배열_크기 }, + () => Math.floor(Math.random() * 99) + 1 +); +const 무작위_도시_목록 = 도시_목록.generate(엄청_큰_배열_크기); + +const 무작위_데이터_리스트 = Array.from({ length: 엄청_큰_배열_크기 }).map( + (_, index) => ({ + name: 무작위_이름_목록[index], + age: 무작위_나이_목록[index], + city: 무작위_도시_목록[index], + }) +); + +// FIXME: 함수 라인 ================================================================== +const timer = { + time: 0, + reset: () => { + this.timer = 0; + }, + start: () => { + this.time = performance.now(); + }, + end: () => { + console.log(`추정 시간: ${performance.now() - this.time}`); + this.time = 0; + }, +}; + +const getMockData = () => Object.assign([], 무작위_데이터_리스트); + +module.exports = { + timer, + getMockData, +}; diff --git a/src/refactoring/areumsheep/utils/non-lazy/_index.js b/src/refactoring/areumsheep/utils/non-lazy/_index.js new file mode 100644 index 0000000..007e0c9 --- /dev/null +++ b/src/refactoring/areumsheep/utils/non-lazy/_index.js @@ -0,0 +1,18 @@ +const { filter } = require('./filter'); +const { map } = require('./map'); +const { reduce } = require('./reduce'); +const { pipe } = require('./pipe'); +const { groupBy } = require('./groupBy'); +const { unique } = require('./unique'); +const { uniqueBy } = require('./uniqueBy'); + +// Non-Lazy의 줄임말 = NL +exports.NL = { + filter, + map, + reduce, + pipe, + groupBy, + unique, + uniqueBy +} \ No newline at end of file diff --git a/src/refactoring/areumsheep/utils/non-lazy/filter.js b/src/refactoring/areumsheep/utils/non-lazy/filter.js new file mode 100644 index 0000000..02c823a --- /dev/null +++ b/src/refactoring/areumsheep/utils/non-lazy/filter.js @@ -0,0 +1,12 @@ +const filter = (array, callback) => { + let i = -1; + const result = []; + + while (++i < array.length) { + const condition = callback(array[i], i, ...array); + if (condition) result.push(array[i]); + } + return result; +}; + +exports.filter = filter; \ No newline at end of file diff --git a/src/refactoring/areumsheep/utils/non-lazy/groupBy.js b/src/refactoring/areumsheep/utils/non-lazy/groupBy.js new file mode 100644 index 0000000..c518b86 --- /dev/null +++ b/src/refactoring/areumsheep/utils/non-lazy/groupBy.js @@ -0,0 +1,18 @@ +const { I } = require('../../internal_utils'); + +const groupBy = (arr, condition) => { + if (!I.isIterable(arr)) { + arr = Object.values(arr); + } + return arr.reduce((acc, value) => { + let key = value[condition]; + if (typeof condition === 'function') { + key = condition(value); + } + acc[key] = acc[key] || []; + acc[key].push(value); + return acc; + }, {}); +}; + +exports.groupBy = groupBy; \ No newline at end of file diff --git a/src/refactoring/areumsheep/utils/non-lazy/map.js b/src/refactoring/areumsheep/utils/non-lazy/map.js new file mode 100644 index 0000000..138c5b9 --- /dev/null +++ b/src/refactoring/areumsheep/utils/non-lazy/map.js @@ -0,0 +1,11 @@ +const map = (array, callback) => { + let i = -1; + const result = []; + + while (++i < array.length) { + result.push(callback(array[i], i, ...array)); + } + return result; +}; + +exports.map = map; diff --git a/src/refactoring/areumsheep/utils/non-lazy/pipe.js b/src/refactoring/areumsheep/utils/non-lazy/pipe.js new file mode 100644 index 0000000..35ff8db --- /dev/null +++ b/src/refactoring/areumsheep/utils/non-lazy/pipe.js @@ -0,0 +1,7 @@ +const { reduce } = require('./reduce'); + +const pipe = (...logics) => { + return (target) => reduce(logics, (value, logic) => logic(value), target); +}; + +exports.pipe = pipe; \ No newline at end of file diff --git a/src/refactoring/areumsheep/utils/non-lazy/reduce.js b/src/refactoring/areumsheep/utils/non-lazy/reduce.js new file mode 100644 index 0000000..b242edf --- /dev/null +++ b/src/refactoring/areumsheep/utils/non-lazy/reduce.js @@ -0,0 +1,20 @@ +const reduce = (array, callback, initialValue) => { + let i = -1; + let returnValue = initialValue; + const arrayLength = array.length; + + if (initialValue === undefined && arrayLength >= 1) { + i++; + returnValue = array[0]; + } + if (returnValue === undefined) { + throw new TypeError('Reduce of empty array with no initial value'); + } + + while (++i < arrayLength) { + returnValue = callback(returnValue, array[i], i, ...array); + } + return returnValue; +}; + +exports.reduce = reduce; \ No newline at end of file diff --git a/src/refactoring/areumsheep/utils/non-lazy/test/filter.test.js b/src/refactoring/areumsheep/utils/non-lazy/test/filter.test.js new file mode 100644 index 0000000..3d3f450 --- /dev/null +++ b/src/refactoring/areumsheep/utils/non-lazy/test/filter.test.js @@ -0,0 +1,31 @@ +const { filter } = require('../filter'); + +describe('Array.prototype.filter()', () => { + it('주어진 함수의 테스트를 통과하는 모든 요소를 모아서 반환한다.', () => { + const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present']; + expect(filter(words, (word => word.length > 6))).toStrictEqual(words.filter(word => word.length > 6)); + }); + + it('기존 배열을 건드리지 않고 새로운 배열을 반환한다.', () => { + const beforeWords = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present']; + const targetWords = beforeWords; + filter(targetWords, (word => word.length > 6)); + expect(targetWords).toStrictEqual(beforeWords); + }); + + it('이미 정의된 함수를 callback 함수로 사용할 수 있다.', () => { + const arr = [ + { id: 15 }, + { id: -1 }, + { id: 0 }, + { id: 3 }, + { id: 12.2 }, + { }, + { id: null }, + { id: NaN }, + { id: 'undefined' } + ]; + const filterByID = ({ id }) => id !== undefined && typeof (id) === 'number' && !isNaN(id); + expect(filter(arr, filterByID)).toStrictEqual(arr.filter(filterByID)); + }); +}); \ No newline at end of file diff --git a/src/refactoring/areumsheep/utils/non-lazy/test/groupBy.test.js b/src/refactoring/areumsheep/utils/non-lazy/test/groupBy.test.js new file mode 100644 index 0000000..482777f --- /dev/null +++ b/src/refactoring/areumsheep/utils/non-lazy/test/groupBy.test.js @@ -0,0 +1,54 @@ +// FIXME: npm test /src/utils/groupBy.test.js + +const { _groupBy } = require('../refactoring/Kay/utils'); + +describe('groupBy 테스트', () => { + describe('non-lazy', () => { + it('case: 1, Normal', () => { + const array = [6.1, 4.2, 6.3]; + const grouped = _groupBy(array, Math.floor); + + expect(grouped).toEqual({ 4: [4.2], 6: [6.1, 6.3] }); + }); + + it('case: 2, Advanced', () => { + const array = [ + [1, 'a'], + [2, 'a'], + [2, 'b'], + ]; + + // 두 번째 인자가 index + const [groupedFirstIndex, groupedSecondIndex] = [ + _groupBy(array, item => item[0]), + _groupBy(array, item => item[1]), + ]; + + expect(groupedFirstIndex).toEqual({ + 1: [[1, 'a']], + 2: [ + [2, 'a'], + [2, 'b'], + ], + }); + + expect(groupedSecondIndex).toEqual({ + a: [ + [1, 'a'], + [2, 'a'], + ], + b: [[2, 'b']], + }); + }); + + it('case: 3, Advanced', () => { + const grouped = _groupBy({ a: 6.1, b: 4.2, c: 6.3 }, Math.floor); + + expect(grouped).toEqual({ 4: [4.2], 6: [6.1, 6.3] }); + }); + }); + + describe('lazy', () => { + // 여기에 Lazy 테스트 코드를 작성해보자!!! + }); +}); diff --git a/src/refactoring/areumsheep/utils/non-lazy/test/map.test.js b/src/refactoring/areumsheep/utils/non-lazy/test/map.test.js new file mode 100644 index 0000000..1959820 --- /dev/null +++ b/src/refactoring/areumsheep/utils/non-lazy/test/map.test.js @@ -0,0 +1,30 @@ +const { map } = require('../map'); + +describe('Array.prototype.map()', () => { + it('callback으로 넘기는 함수로 계산 후 새로운 배열을 반환한다.', () => { + const arr = [1, 2, 3, 4, 5]; + expect(map(arr, (x => x * 2))).toStrictEqual(arr.map(x => x * 2)); + }); + + it('계산 후에도 이전 값은 변화가 없다.', () => { + const beforeArray = [1, 2, 3, 4, 5]; + const targetArray = beforeArray; + map(targetArray, (x => x * 2)); + expect(targetArray).toStrictEqual(beforeArray); +}); + + it('이미 정의되어 있는 메서드를 callback 함수로 사용할 수 있다.', () => { + const arr = [1, 2, 3, 4, 5]; + expect(map(arr, Math.sqrt)).toStrictEqual(arr.map(Math.sqrt)); + }); + + it('배열 속 객체를 재구성할 수 있다.', () => { + const arr = [{ key: 1, value: 10 }, { key: 2, value: 20 }, { key: 3, value: 30 }]; + const reformatLogic = ({ key, value }) => { + let tempObject = {}; + tempObject[key] = value; + return tempObject; + } + expect(map(arr, reformatLogic)).toStrictEqual(arr.map(reformatLogic)); + }); +}); diff --git a/src/refactoring/areumsheep/utils/non-lazy/test/pipe.test.js b/src/refactoring/areumsheep/utils/non-lazy/test/pipe.test.js new file mode 100644 index 0000000..1ed4e87 --- /dev/null +++ b/src/refactoring/areumsheep/utils/non-lazy/test/pipe.test.js @@ -0,0 +1,11 @@ +const { pipe } = require('../pipe'); + +describe('pipe()', () => { + it('pipe에 들어가는 함수는 순차적으로 실행된다.', () => { + const target = 30; + const double = (x) => x * 2; + const addOne = (x) => x + 1; + + expect(pipe(double, addOne)(target)).toEqual(addOne(double(target))); + }); +}); \ No newline at end of file diff --git a/src/refactoring/areumsheep/utils/non-lazy/test/reduce.test.js b/src/refactoring/areumsheep/utils/non-lazy/test/reduce.test.js new file mode 100644 index 0000000..c7cbcca --- /dev/null +++ b/src/refactoring/areumsheep/utils/non-lazy/test/reduce.test.js @@ -0,0 +1,34 @@ +const { reduce } = require('../reduce'); + +describe('Array.prototype.reduce()', () => { + it('배열의 각 요소에 대해 주어진 리듀서 함수를 실행하고, 하나의 결과값을 반환한다.', () => { + expect(reduce([3, 3, 3], (a, b) => a + b, 1)).toEqual([3, 3, 3].reduce((a, b) => a + b, 1)); + }); + + it('기존 배열을 건드리지 않고 새로운 배열을 반환한다.', () => { + const beforeArray = [1, 2, 3, 4, 5]; + const targetArray = beforeArray; + reduce(targetArray, (prev, cur) => prev + cur, 1) + expect(targetArray).toStrictEqual(beforeArray); + }) + + it('이미 정의된 함수를 callback 함수로 사용할 수 있다.', () => { + const arr = [ + [0, 1], + [2, 3], + [4, 5], + ]; + const formatLogic = (accumulator, currentValue) => accumulator.concat(currentValue); + expect(reduce(arr, formatLogic, [])).toStrictEqual(arr.reduce(formatLogic, [])); + }); + + it('초기 값을 지정하지 않아도 정상 계산되어야 한다.', () => { + expect(reduce([2, 2, 2], (a, b) => a * b)).toEqual([2, 2, 2].reduce((a, b) => a * b)); + }); + + it('빈 배열을 초기 값 없이 사용할 경우 TypeError가 발생한다.', () => { + expect(() => { + reduce([], (value) => value) + }).toThrow(TypeError); + }); +}); \ No newline at end of file diff --git a/src/refactoring/areumsheep/utils/non-lazy/test/unique.test.js b/src/refactoring/areumsheep/utils/non-lazy/test/unique.test.js new file mode 100644 index 0000000..71dc403 --- /dev/null +++ b/src/refactoring/areumsheep/utils/non-lazy/test/unique.test.js @@ -0,0 +1,39 @@ +// FIXME: npm test /src/utils/unique.test.js +const { unique } = require('../unique'); + +describe('unique 테스트', () => { + describe('non-lazy', () => { + it('case: 1, Normal', () => { + const [firstArray, secondArray] = [ + [2, 1, 2], + [1, 2, 1], + ]; + const firstUniqueArray = unique(firstArray); + const secondUniqueArray = unique(secondArray); + + expect(firstUniqueArray).toEqual([2, 1]); + expect(secondUniqueArray).toEqual([1, 2]); + }); + + it('case: 2, Advanced', () => { + const [firstArray, secondArray, thirdArray] = [ + [1, 2, 3], + [1, 1, 2, 2, 3], + [1, 2, 3, 3, 3, 3, 3], + ]; + const firstUniqueArray = unique(firstArray, undefined, true); + const secondUniqueArray = unique(secondArray, undefined, true); + const thirdUniqueArray = unique(thirdArray, undefined, true); + + expect(firstUniqueArray).toEqual([1, 2, 3]); + expect(secondUniqueArray).toEqual([1, 2, 3]); + expect(thirdUniqueArray).toEqual([1, 2, 3]); + }); + + it('case: 3, arr is not iterable', () => { + const errorObjects = unique({ a: 'a', b: 'b' }); + + expect(errorObjects).toEqual([]); + }); + }); +}); diff --git a/src/refactoring/areumsheep/utils/non-lazy/test/uniqueBy.test.js b/src/refactoring/areumsheep/utils/non-lazy/test/uniqueBy.test.js new file mode 100644 index 0000000..4085472 --- /dev/null +++ b/src/refactoring/areumsheep/utils/non-lazy/test/uniqueBy.test.js @@ -0,0 +1,26 @@ +// FIXME: npm test /src/utils/unique.test.js +const { uniqueBy } = require('../uniqueBy'); + +describe('uniqueBy 테스트', () => { + describe('non-lazy', () => { + it('case: 1, Advanced', () => { + const objects = [ + { x: 1, y: 2 }, + { x: 2, y: 1 }, + { x: 1, y: 2 }, + ]; + const uniqueObjects = uniqueBy(objects); + + expect(uniqueObjects).toEqual([ + { x: 1, y: 2 }, + { x: 2, y: 1 }, + ]); + }); + + it('case: 2, arr is not iterable', () => { + const errorObjects = uniqueBy({ a: 'a', b: 'b' }); + + expect(errorObjects).toEqual([]); + }); + }); +}); diff --git a/src/refactoring/areumsheep/utils/non-lazy/unique.js b/src/refactoring/areumsheep/utils/non-lazy/unique.js new file mode 100644 index 0000000..df06c99 --- /dev/null +++ b/src/refactoring/areumsheep/utils/non-lazy/unique.js @@ -0,0 +1,10 @@ +const { I } = require('../../internal_utils'); + +const unique = (arr) => { + if (!I.isIterable(arr)) { + return []; + } + return [...new Set(arr)]; +} + +exports.unique = unique; \ No newline at end of file diff --git a/src/refactoring/areumsheep/utils/non-lazy/uniqueBy.js b/src/refactoring/areumsheep/utils/non-lazy/uniqueBy.js new file mode 100644 index 0000000..451c4c1 --- /dev/null +++ b/src/refactoring/areumsheep/utils/non-lazy/uniqueBy.js @@ -0,0 +1,28 @@ +const { I } = require('../../internal_utils'); + +const uniqueBy = (arr, callback, isSorted) => { + if (!I.isIterable(arr)) { + return []; + } + const map = new Map(); + return arr.reduce((acc, item) => { + let key; + // Object인 경우 + if (!Array.isArray(item)) { + key = JSON.stringify(item); + } + // callback이 있는 경우 + if (typeof callback === 'function') { + key = callback(item); + } + + if (!map.has(key)) { + acc.push(item); + } + map.set(key, item); + + return acc; + }, []); +}; + +exports.uniqueBy = uniqueBy; \ No newline at end of file diff --git "a/src/refactoring/areumsheep/\354\210\230\354\240\225\355\233\204-01_\354\241\260\352\261\264\353\266\200_\353\263\265\354\236\241\354\204\261.js" "b/src/refactoring/areumsheep/\354\210\230\354\240\225\355\233\204-01_\354\241\260\352\261\264\353\266\200_\353\263\265\354\236\241\354\204\261.js" new file mode 100644 index 0000000..33240dd --- /dev/null +++ "b/src/refactoring/areumsheep/\354\210\230\354\240\225\355\233\204-01_\354\241\260\352\261\264\353\266\200_\353\263\265\354\236\241\354\204\261.js" @@ -0,0 +1,97 @@ +// 동물에 맞는 이모티콘 출력 +const getAnimalEmoji = (animal) => { + const EMOJI_ANIMAL = { + dog: '🐶', + cat: '🐱', + frog: '🐸', + panda: '🐼', + giraffe: '🦒', + monkey: '🐵', + unicorn: '🦄', + dragon: '🐲', + }; + return EMOJI_ANIMAL[animal]; +}; +console.log(getAnimalEmoji('dragon')); + +// dog, cat인 경우 문장 출력 +const printMyAnimal = (animal) => { + if (animal === 'dog' || animal === 'cat') { + return `I have a ${animal}`; + } + return "I don't have a animal"; +}; +console.log(printMyAnimal('dog')); + +// 동물 정보 출력 +const getAnimalDetails = (animal) => { + if (!animal) { + return 'No animal'; + } + if (!animal.type) { + return 'No animal type'; + } + if (!animal.name) { + return 'No animal name'; + } + if (!animal.gender) { + return 'No animal gender'; + } + return `${animal.name} is a ${animal.gender} ${animal.type}`; +}; +console.log(getAnimalDetails()); +console.log(getAnimalDetails({ type: 'dog', gender: 'female' })); +console.log(getAnimalDetails({ type: 'dog', name: 'Lucy' })); +console.log(getAnimalDetails({ type: 'dog', name: 'Lucy', gender: 'female' })); + +// 과일 색 출력 +const printFruits = (color = []) => { + switch (color) { + case 'red': + return ['apple', 'strawberry']; + case 'yellow': + return ['banana', 'pineapple']; + case 'purple': + return ['grape', 'plum']; + } +}; +console.log(printFruits(null)); +console.log(printFruits('yellow')); + +// 야채 이름 출력 +const printVegetableName = (vegetable) => { + if (!(vegetable && vegetable.name)) { + console.log('unknown'); + return; + } + console.log(vegetable.name); +}; +printVegetableName(undefined); +printVegetableName({}); +printVegetableName({ name: 'cabbage', quantity: 2 }); + +// 자동차 프로퍼티 확인 +const car = { + model: 'Fiesta', + manufacturer: { + name: 'Ford', + address: { + street: 'Some Street Name', + number: '5555', + state: 'USA', + }, + }, +}; + +const model = car?.model || 'default model'; +const street = car?.manufacturer?.address?.street || 'default street'; +const phoneNumber = car?.manufacturer?.address && car?.manufacturer?.phoneNumber; + +console.log(model); +console.log(street); +console.log(phoneNumber); + +const isManufacturerFromUSA = () => { + console.log(car?.manufacturer?.address?.state === 'USA'); +}; +console.log(isManufacturerFromUSA()); \ No newline at end of file diff --git "a/src/refactoring/areumsheep/\354\210\230\354\240\225\355\233\204-02_\352\270\260\353\212\245_\355\212\271\354\240\225\355\225\230\352\270\260.js" "b/src/refactoring/areumsheep/\354\210\230\354\240\225\355\233\204-02_\352\270\260\353\212\245_\355\212\271\354\240\225\355\225\230\352\270\260.js" new file mode 100644 index 0000000..ad9316f --- /dev/null +++ "b/src/refactoring/areumsheep/\354\210\230\354\240\225\355\233\204-02_\352\270\260\353\212\245_\355\212\271\354\240\225\355\225\230\352\270\260.js" @@ -0,0 +1,100 @@ +function beforePrinter(user, phone) { + console.log('USER PHONE:'); + console.log(`Country code Phone : ${phone.countryCode}`); + console.log(`Phone area code: ${phone.areaCode}`); + console.log(`Phone base number: ${phone.baseNumber}\n`); + console.log('USER DATA:'); + console.log(`User name: ${user.userName}`); + console.log(`User lastname: ${user.userLastName}`); + console.log(`User DNI: ${user.userDni}`); + console.log(`User phone: ${user.userEmail}`); + console.log(`User email: ${user.userPhone}`); +} + +function afterPrinter(user, phone) { + console.log('USER PHONE:'); + console.log(`Country code Phone : ${phone.getCountryCode()}`); + console.log(`Phone: ${phone.getFormatPhone()}\n`); + console.log('USER DATA:'); + console.log(`User name: ${user.getName()}`); + console.log(`User lastname: ${user.getLastName()}`); + console.log(`User DNI: ${user.getDni()}`); + console.log(`User email: ${user.getEmail()}`); +} + +class Phone { + constructor(unformattedNumber) { + this.unformattedNumber = unformattedNumber; + } + + get countryCode() { + return this.unformattedNumber.substring(0, 3); + } + get areaCode() { + return `${this.unformattedNumber.substring(3, 6)}`; + } + get baseNumber() { + return `${this.unformattedNumber.substring(6, 9)} ${this.unformattedNumber.substring(9, 12)}`; + } + + getCountryCode() { + return this.countryCode; + } + getFormatPhone() { + return `${this.countryCode} ${this.areaCode} ${this.baseNumber}` + } +} + +class User { + constructor(name, lastName, dni, phone, email) { + this.name = name; + this.lastName = lastName; + this.dni = dni; + this.email = email; + this.phone = phone.getFormatPhone(); + } + + get userName() { + return this.name; + } + get userLastName() { + return this.lastName; + } + get userDni() { + return this.dni; + } + get userEmail() { + return this.email; + } + get userPhone() { + return this.phone; + } + + getName() { + return this.userName; + } + getLastName() { + return this.userLastName; + } + getDni() { + return this.userDni; + } + getPhone() { + return this.phone; + } + getEmail() { + return this.userEmail; + } +} + +const phone1 = new Phone('+34635538973'); +const user1 = new User( + 'Fernando', + 'Aparicio Galende', + '12345678S', + phone1, + 'fernando.aparicio@guidesmiths.com' +); + +beforePrinter(user1, phone1); +afterPrinter(user1, phone1); \ No newline at end of file diff --git "a/src/refactoring/areumsheep/\354\210\230\354\240\225\355\233\204-03_\353\266\200\354\240\201\354\240\210\355\225\234_\355\217\211\352\260\200.js" "b/src/refactoring/areumsheep/\354\210\230\354\240\225\355\233\204-03_\353\266\200\354\240\201\354\240\210\355\225\234_\355\217\211\352\260\200.js" new file mode 100644 index 0000000..ea50065 --- /dev/null +++ "b/src/refactoring/areumsheep/\354\210\230\354\240\225\355\233\204-03_\353\266\200\354\240\201\354\240\210\355\225\234_\355\217\211\352\260\200.js" @@ -0,0 +1,97 @@ +const per2Int = (value, per) => (value * per) / 100; + +const Client = ({ name, type, location }) => { + const rate = Object.freeze({ + normal: 0, + premium: 20, + }); + this.name = name; + this.type = type; + this.location = location; + + getName = () => this.name; + getLocation = () => this.location; + getTypeByRate = () => rate[this.type]; + + return { + getName, + getLocation, + getTypeByRate, + }; +}; + +const Product = ({ value, title, shipping }) => { + this.value = value; + this.title = title; + this.shipping = shipping; + + getValue = () => this.value; + getTitle = () => this.title; + getShipping = () => this.shipping; + getPrice = (rate) => this.value - per2Int(this.value, rate); + + return { + getValue, + getTitle, + getShipping, + getPrice, + }; +}; + +const Order = ({ id, value, client, product }) => { + const taxes = Object.freeze({ + EU: 21, + USA: 14, + }); + this.id = id; + this.value = value; + this.client = client; + this.product = product; + + getId = () => this.id; + getValue = () => this.value; + getClientName = () => this.client.getName(); + getProductName = () => this.product.getTitle(); + getShipping = () => this.product.getShipping(); + getTotalPriceByLocation = () => + this.product.getPrice(this.client.getTypeByRate()) + + taxes[this.client.getLocation()]; + + return { + getId, + getValue, + getClientName, + getProductName, + getShipping, + getTotalPriceByLocation, + }; +}; + +const Print = () => { + printSummary = ({ order }) => { + return `Order: ${order.getId()} + Client: ${order.getClientName()} + Product: ${order.getProductName()} + TotalAmount: ${order.getTotalPriceByLocation()} + + Arrival in: ${order.getShipping()}`; + }; + + return { + printSummary, + }; +}; + + +const product = Product({ value: 10000, title: 'product', shipping: 123 }); +const client = Client({ name: 'areumsheep7', type: 'normal', location: 'EU' }); +const order = Order({ + id: '000123', + value: 3000, + client, + product, +}); + +const print = Print(); + +console.log(print.printSummary({ order }));