Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[siny] unique, groupBy #47

Open
wants to merge 2 commits into
base: 2-team
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions src/refactoring/siny/MyPromise.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// new 키워드로 생성한다.
/**
* 기본형
* new MyPromise((res,rej)=>{
* setTimeout(()=>{
*
* res('값');
* },100)
* }).then(data)
*
* 1. 해당 클래스의 constructor 가 받는 함수는 res,rej를 반환하는 일급함수이다.
* 2. res는 실행 이후에 then,catch,finally 메서드를 실행할 수 있다.
* 3. res나 rej를 실행하기 전에 then, catch,finally를 실행하면, 지연실행이 된다.
* 4. then의 첫번째 인자인 일급함수를 실행하면 fullfiled가 되고, 해당 함수가 반환하는 값을 value로 갖는다.
* 5. then의 두번째 인자인 일급함수를 실행하면, rejected가 되고 해당함수가 반환하는 에러를 value로 갖는다.
* 6. catch의 첫번째 인자인 일급함수를 실행하면, rejected가 되고 해당함수가 반환하는 에러를 value로 갖는다.
*
* */

class MyPromise {
#value;
#status;
#error;
#PENDING = 'pending';
#FULFILLED = 'fulfilled';
#REJECTED = 'rejected';
#fulfilledCallbacks = [];
constructor(executor) {
if (executor === undefined || executor === null) {
throw new Error('executor 필수');
}
if (typeof executor !== 'function') {
throw new Error('executor는 꼭 함수');
}
this.#status = this.#PENDING;
executor(this.resolve.bind(this), this.reject.bind(this));
}

reject(error) {
if (this.#status === this.#PENDING) {
this.#error = error;
this.#status = this.#REJECTED;
}
}
resolve(data) {
if (this.#status === this.#PENDING) {
this.#value = data;
this.#status = this.#FULFILLED;
this.#fulfilledCallbacks.forEach(cb => cb(this.#value));
}
}

then(onRes, onRej) {
const promise = new MyPromise((res, rej) => {
if (this.#status === this.#PENDING) {
this.#fulfilledCallbacks.push(value => {
setTimeout(() => {
onRes(value);
});
});
}
if (this.#status === this.#FULFILLED) {
res(onRes(this.#value));
}
if (this.#status === this.#REJECTED) {
rej(onRej(this.#error));
}
});
return promise;
}
}

const sleep = ms =>
new Promise(res => {
setTimeout(res, ms);
});

describe('MyPromise', () => {
it('new MyPromise() 를 실행하면 에러를 반환한다,', () => {
expect(() => new MyPromise()).toThrow();
});
it('new MyPromise(1) 를 실행하면 에러를 반환한다,', () => {
expect(() => new MyPromise(1)).toThrow('executor는 꼭 함수');
});
it('new MyPromise(() => {})를 실행하면 에러가 발생하지 않는다. ', () => {
expect(() => new MyPromise(() => {})).not.toThrow();
});
it('실행해봐', async () => {
let value;
new MyPromise((res, rej) => {
setTimeout(() => {
res(123);
}, 500);
}).then(data => (value = data));
await sleep(1000);
expect(value).toEqual(123);
});
});
8 changes: 3 additions & 5 deletions src/refactoring/siny/ps/solution1.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
const { reduce, pipe, curry2, sort, filter } = require('../utils.js');
const { reduce, pipe, curry2, sort, G } = require('../utils.js');
const curredSort = curry2(sort);
const curredFilter = curry2(filter);

const solution1 = arr => {
const calcMulti = (acc, item, i) => {
if (i % 2) return acc;
Expand All @@ -21,8 +19,8 @@ const solution1 = arr => {

const _중간결과 = pipe(calcAddByArr, calcMultiByArr)(arr);
const sortByAsc = curredSort((a, b) => a - b);
const _2의_배수만_filter = curredFilter(item => !(item % 2));
const _3의_배수만_filter = curredFilter(item => !(item % 3));
const _2의_배수만_filter = G.filter(item => !(item % 2));
const _3의_배수만_filter = G.filter(item => !(item % 3));
return {
t: pipe(_2의_배수만_filter, sortByAsc)(_중간결과),
h: pipe(_3의_배수만_filter, sortByAsc)(_중간결과),
Expand Down
2 changes: 1 addition & 1 deletion src/refactoring/siny/ps/solution2.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const solution2 = str => {
const splitByComma = curredSplit(',');
const addToSet = (set, key) => set.add(key);
const compareTo_글자수가_긴것_우선정렬_글자수가_같으면_알파벳빠른순 = (a, b) =>
(b.length === a.length && a.charCodeAt(0) - b.charCodeAt(0)) || b.length - a.length;
(b.length === a.length && a < b ? -1 : 1) || b.length - a.length;

return pipe(
splitByComma,
Expand Down
92 changes: 92 additions & 0 deletions src/refactoring/siny/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,96 @@ const toArray = obj => {
const trim = str => str.trim();
const split = (separator, str) => str.split(separator);

const G = {};

function* gMap(fn, arr) {
for (const item of arr) {
yield fn(item);
}
}

function* gFilter(fn, arr) {
for (const item of arr) {
if (fn(item)) yield item;
}
}

function* gTake(num, arr) {
let count = 0;
for (const item of arr) {
if (count++ !== num) yield item;
}
}

function* gTakeWhile(fn, arr) {
for (const item of arr) {
if (fn(item)) yield item;
}
}

function* gRange(end) {
let start = 0;
while (start !== end) {
yield start++;
}
}

function* gSkip(n, arr) {
let count = 0;
for (const item of arr) {
if (count++ >= n) yield item;
}
}

G.map = curry2(gMap);
G.filter = curry2(gFilter);
G.range = gRange;
G.take = curry2(gTake);
G.takeWhile = curry2(gTakeWhile);
G.skip = curry2(gSkip);

function groupBy(iter, callback) {
const newObj = {};
const genKey = item => {
if (typeof callback === 'function') {
return callback(item);
} else {
// callback은 index 혹은 key
return item[callback];
}
};
const genIter = anIter => {
if (anIter instanceof Array) {
return anIter;
}
if (anIter instanceof Object) {
return Object.values(anIter);
}
return anIter;
};
for (const item of genIter(iter)) {
let key = genKey(item);

newObj[key] = newObj?.[key] === undefined ? [item] : [...newObj[key], item];
}
return newObj;
}

function* gUnique(arr) {
const map = new Map();
if (arr === undefined || arr === null) {
throw new Error('첫번째 인자는 꼭 필요합니다.');
}
for (const item of arr) {
const key = JSON.stringify(item);
if (!map.has(key)) {
yield item;
}
map.set(key, item);
}
}

G.unique = gUnique;
module.exports = {
filter,
reduce,
Expand All @@ -71,4 +161,6 @@ module.exports = {
curry2,
curry3,
toArray,
groupBy,
G,
};
8 changes: 4 additions & 4 deletions src/utils/groupBy.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
// FIXME: npm test /src/utils/groupBy.test.js

const { timer } = require('../utils/lib.js');
const { groupBy } = require('../refactoring/siny/utils');
// 어떤 것을 해볼까요?
const groupBy = (arr, callback) => {
return arr;
};

describe('groupBy 테스트', () => {
describe('non-lazy', () => {
Expand Down Expand Up @@ -42,7 +40,9 @@ describe('groupBy 테스트', () => {
});

it('case: 3, Advanced', () => {
timer.start();
const grouped = groupBy({ a: 6.1, b: 4.2, c: 6.3 }, Math.floor);
timer.end();

expect(grouped).toEqual({ 4: [4.2], 6: [6.1, 6.3] });
});
Expand Down
5 changes: 3 additions & 2 deletions src/utils/lib.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const { G, map } = require('../refactoring/siny/utils.js');

// FIXME: 프로토타입 =================================================================
Array.prototype.generate = function (size = 10) {
let returnArray = [];
Expand All @@ -20,7 +22,7 @@ Array.prototype.shuffle = function () {
};

// FIXME: 상수 라인===================================================================
const 엄청_큰_배열_크기 = 1000;
const 엄청_큰_배열_크기 = 10000;

const 이름_목록 = [
'Zachary',
Expand Down Expand Up @@ -363,7 +365,6 @@ const timer = {
};

const getMockData = () => Object.assign([], 무작위_데이터_리스트);

module.exports = {
timer,
getMockData,
Expand Down
9 changes: 5 additions & 4 deletions src/utils/unique.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// FIXME: npm test /src/utils/unique.test.js

// 어떤 것을 해볼까요?
const unique = (arr, callback, isSorted) => {
return arr;
};

const { G } = require('../refactoring/siny/utils.js');
const gUnique = G.unique;
const unique = (arr, callback, isSorted) => (isSorted ? [...gUnique(arr)].sort() : [...gUnique(arr)]);

describe('unique 테스트', () => {
describe('non-lazy', () => {
Expand All @@ -21,7 +22,7 @@ describe('unique 테스트', () => {

it('case: 2, Advanced', () => {
const [firstArray, secondArray, thirdArray] = [
[1, 2, 3],
[3, 2, 1],
[1, 1, 2, 2, 3],
[1, 2, 3, 3, 3, 3, 3],
];
Expand Down