Modify JS object functionally and precisely
Install from the NPM repository using yarn or npm:
npm i functional-flattener
yarn add functional-flattener
fucntional-flattener
λ JSμ Array
κ° map
, filter
λ±μ Array.prototype
λ©μλλ₯Ό μ΄μ©νμ¬ μμ λ μ μλ κ²κ³Ό κ°μ μ리λ‘, ν¨μν νλ‘κ·Έλλ°μ ν΅ν΄ JS Object
λ₯Ό μμ νκΈ° μν΄ λ§λ€μ΄μ‘μ΅λλ€. JS Object
λ₯Ό κ°λ°μμ μλμ λ§κ² μμ ν μ μλλ‘ νλ λͺ κ°μ§μ λ©μλλ€κ³Ό, ν¨μν νλ‘κ·Έλλ°μμ μκ°μ λ°μ λ©μλ 체μ΄λμ μ§μν©λλ€.
- μλ²μ ν΄λΌμ΄μΈνΈ μ±μ ꡬν μΈμ΄κ° λ€λ₯Έλ° μλ²μμ λ°λ‘ μ²λ¦¬νμ§ μμ κ²½μ°μ HTTPμμ²μ ν΄μ λ°μ JSON κ°μ²΄μ keyκ°μ΄ JSμμ μ£Όλ‘ μ¬μ©νλ μΉ΄λ©μΌμ΄μ€κ° μλ λ€λ₯Έ μΌμ΄μ€μΌ κ²½μ°κ° μμ΅λλ€(Djangoμ κ²½μ°λ μ€λ€μ΄ν¬ μΌμ΄μ€)
- NodeκΈ°λ° ν΄λΌμ΄μΈνΈμμ ESLintλ₯Ό μ μ©μ€μ΄λΌλ©΄, μΉ΄λ©μΌμ΄μ€κ° μλ λ€λ₯Έ μΌμ΄μ€μ ν€λ₯Ό μ°Έμ‘°νλ κ² λ§μΌλ‘λ μλ¬κ° λ°μν©λλ€. λ°λΌμ JSON κ°μ²΄μ ν€λ₯Ό μΌκ΄μ μΌλ‘ μΌμ΄μ±ν μ μλ λκ΅¬κ° νμν©λλ€.
functional-flattener
λ κ°μ²΄μ λͺ¨λkey
λ₯Ό μΌκ΄μ μΌλ‘ μΉ΄λ©, μ€λ€μ΄ν¬ μΌμ΄μ€λ‘ casingνλ λ©μλλ₯Ό μ§μν©λλ€.
- HTTP μμ²μ ν΅ν΄ JSON κ°μ²΄λ₯Ό λ°μμ λ ν΄λΉ κ°μ²΄λ₯Ό ν΄λΌμ΄μΈνΈ μ±μμ μ°κΈ° μ’λλ‘ μμ μ ν΄μΌνλ μν©μ΄ μμ΅λλ€.
- TravelFlan FEνμμλ μ΄λ κ² μλ²λ‘λΆν° μλ΅λ°μ κ°μ²΄λ₯Ό ν΄λΌμ΄μΈνΈμ
use-case
μ λ§κ² μμ νλ μμν¨μλ₯Όflattener
λΌκ³ λͺ λͺ νκ³ , κ΄μ΅μ μΌλ‘ μ¬μ©νκ³ μμ΅λλ€. functional-flattener
λ μμμ μΈκΈν μΌκ΄μ μΈ μΌμ΄μ±λΏλ§ μλλΌ κ°μ²΄λ₯Ό ν¨κ³Όμ μΌλ‘ μμ ν μ μλ λ©μλλ€μ μ 곡νλ©°, λ©μλ 체μ΄λμ ν΅ν΄ κ°μ²΄κ° μμ λλ κ³Όμ λ€μ ν¨κ³Όμ μΌλ‘ μ μ©ν μ μκ² νμ΅λλ€.
const mockData = {
userId: 12424,
userName: 'max',
userAge: 25,
userProfile: {
userProfileText: 'I Love Zebra',
userFavoriteAnimal: { id: 3, animalName: 'vulture' },
userFriends: [
{ id: 12324, name: 'julie', favoriteAnimal: { id: 0, animalName: 'tiger' } },
{ id: 11424, name: 'michael', favoriteAnimal: { id: 1, animalName: 'lion' } },
{ id: 18924, name: 'shawn', favoriteAnimal: { id: 2, animalName: 'monkey' } },
],
},
}
function flattener(data) {
const { userId, userName, userAge, userProfile } = data
const processedUserName = `Hi! I am ${userName}`
const processedUserProfile = {
...userProfile,
userFriends: userProfile.userFriends.map((friend) => {
...friend,
name: `I am your friend name ${friend.name}.`
})
}
return {
userId,
userName : processedUserName,
userAge,
userProfile: processedUserProfile
}
}
- λͺ
λ Ήν νλ‘κ·Έλλ°μΌλ‘ κ°μ²΄λ₯Ό μΌμΌν νμ΄ν€μ³ μλ‘μ΄ κ°μ²΄λ₯Ό λ§λ€μ΄ 리ν΄νλ μμ
imperative flattener
μ λ‘μ§μ μ₯ν©νκ³ , κ°λ μ±μλ μ’μ§ μμ΅λλ€. - μν©μ λ°λΌ μ μ²λ¦¬κ° νμ μλ κ°μ²΄μ ν€μ νλ‘νΌν°λ₯Ό νμ΄νν μλ μκ³ , νμ΄νν΄μΌν λ³μλ λμ΄λ μ μμ΅λλ€.
- ν¨μν νλ‘κ·Έλλ°κ³Ό λ©μλ 체μ΄λμ μ μ©νλ©΄ κ°μ²΄ μ 체λ₯Ό νμ΄ν€μΉ νμ μμ΄ μ μ²λ¦¬κ° νμν νλ‘νΌν°μλ§ μ κ·Όμ΄ κ°λ₯ν©λλ€.
- flatteningμ νμν λ©μλλ₯Ό μ 곡νλ ν΄λμ€μ λλ€.
- λ©€λ²λ³μλ‘ μΈμ€ν΄μ€ μμ±μμ νλΌλ―Έν°λ‘ μ 곡ν΄μΌ νλ
Target Object
λ₯Ό κ°μ§κ³ μμ΅λλ€. - μ΄ ν΄λμ€μ λͺ¨λ λ©μλλ€μ μμνκ² μλ‘μ΄ FlattenTarget ν΄λμ€μ μΈμ€ν΄μ€λ₯Ό 리ν΄νκΈ° λλ¬Έμ, λ©μλ 체μ΄λμ΄ κ°λ₯ν©λλ€.
returnResult()
λ©μλλ₯Ό μ¬μ©ν΄μΌλ§ μΌλ° κ°μ²΄λ₯Ό λ°νν©λλ€.
flattener({
userId: 12424,
userName: 'max',
userAge: 25,
})//...λ€λ₯Έ λ©μλλ€μ 체μ΄λν©λλ€.
- FlattenTarget μΈμ€ν΄μ€λ₯Ό λ§λ€μ΄ 리ν΄νλ ν©ν 리 ν¨μμ λλ€. λΌμ΄λΈλ¬λ¦¬λ₯Ό μ΄μ©ν΄ κ°μ²΄λ₯Ό λ³κ²½νλ νμμ μμμ μ΄ λλ ν¨μμ λλ€.
- FlattenTarget μΈμ€ν΄μ€λ₯Ό 리ν΄νκΈ° λλ¬Έμ, λ°λ‘ FlattenTarget ν΄λμ€μ λ©μλλ€μ 체μ΄λν μ μμ΅λλ€.
- μΈμλ‘ μλ°μ€ν¬λ¦½νΈ κ°μ²΄λ₯Ό λ°μ΅λλ€.
const result = flattener({
user_id: 12424,
user_name: 'Max',
user_age: 25,
// casingOptionμ camelκ³Ό snakeλ₯Ό μ§μν©λλ€.
}).case({to:'camel'}).returnResult()
// Result will be { userId: 12424, userName: 'Max', userAge: 25 }
- case λ©μλλ target κ°μ²΄μ keyλ₯Ό μΌκ΄μ μΌλ‘ μΌμ΄μ±νλ λ©μλμ λλ€.
- μΈμλ‘ casingOption κ°μ²΄λ₯Ό λ°μ΅λλ€. κ°μ²΄μ to νλ‘νΌν°λ‘ 'camel' νΉμ 'snake' μ΅μ μ μ£Όμ΄ κ°μ²΄μ λͺ¨λ ν€μ μΌμ΄μ±μ μΌκ΄μ μΌλ‘ λ°κΏ μ μμ΅λλ€.
- μ΄λ―Έ κ°μ²΄μ keyκ° casingOptionμμ λͺ μλ μΌμ΄μ±μ΄λΌλ©΄, ν΄λΉ keyλ₯Ό 무μν©λλ€.
const result = flattener({
userId: 12424,
userName: 'Max',
userAge: 25,
userProfile: {
userIntroduce: 'Hi! My name is Max',
userFavoriteAnimal: 'zebra',
}
// changeKeyPlanμ keyλ κΈ°μ‘΄ targetμ key, valueλ μλ‘μ΄ keyμ
λλ€.
// κ°μ²΄ νλ‘νΌν°μ keyλ₯Ό λ°κΎΈκ³ μΆμ κ²½μ°, ':'μ μ΄μ©ν©λλ€.
}).changeKey({
userAge:'userCurrentName',
'userProfile:userInfo': {
userFavoriteAnimal: 'userAnimal'
}
}).returnResult()
/*
Result will be
{
userId: 12424,
userName: 'Max',
userCurrentAge: 25,
userInfo: {
userIntroduce: 'Hi! My name is Max',
userAnimal: 'zebra',
}
}
*/
- μ΄λ―Έ μ‘΄μ¬νλ κ°μ²΄ νλ‘νΌν°μ keyλ₯Ό, λ©μλμ λκΈ°λ
changeKeyPlan
νλΌλ―Έν°μ λͺ μλ κ°μΌλ‘ λ°κΏλλ€. changeKeyPlan
μ κ°μ²΄μ΄λ©° κΈ°λ³Έμ μΌλ‘λ κ΅μ²΄ν targetμ keyλ₯Ό planμ keyλ‘, μλ‘μ΄ keyλ₯Ό λ¬Έμμ΄ κ°μΌλ‘ κ°μ§λλ€.- λ€λ§ λ°λμ΄μΌν targetμ νΉμ keyκ° κ°μ²΄λ₯Ό κ°μΌλ‘ κ°μ§λ€λ©΄, λ¬Έμμ΄ κ°μ λͺ
μν΄μ€ μ μκΈ° λλ¬Έμ μ½λ‘ (
:
)μ μ¬μ©νμ¬ κ΅μ²΄ν targetμ ν€μ ν¨κ» μλ‘μ΄ ν€λ₯Ό λͺ μν΄μ€ μ μμ΅λλ€. - TypeScript μ¬μ©μ
ChangeKeyPlan
νμ μ μ°Έμ‘°ν μ μμ΅λλ€.
const result = flattener({
userId: 12424,
userName: 'Max',
userAge: 25,
userProfile: {
userIntroduce: 'I love zebra.',
userFavoriteAnimal: 'zebra',
}
}).process((target:Target) => ({
userId: 10000,
userAge: (age:number) => `${age} years old`,
userProfile: {
userIntroduce: (text:string) => `Hello! My name is ${target.userName}. ${text}`,
},
})).returnResult()
/*
Result will be
{
userId: 10000
userName: 'Max',
userAge: '25 years old',
userProfile: {
userIntroduce: 'Hello! My name is Max. I love zebra.',
userFavoriteAnimal: 'zebra',
}
}
*/
- μ΄λ―Έ μ‘΄μ¬νλ κ°μ²΄ νλ‘νΌν°λ€μ κ°μ λ©μλμ λκΈ°λ
processPlan
νλΌλ―Έν°μ λͺ μλ κ°μΌλ‘ κ°κ³΅νκ±°λ, λͺ μλ ν¨μλ₯Ό μ€νν κ°μΌλ‘ κ°κ³΅νλ λ©μλμ λλ€. processPlan
νλΌλ―Έν°λ target objectλ₯Ό μΈμλ‘ λ°λ ν¨μμ λλ€. μ΄ ν¨μκ° λ¦¬ν΄νλ κ°μ²΄μ νλ‘νΌν°λ€μ target objectλ₯Ό μ°Έμ‘°ν μ μμ΅λλ€.processPlan
ν¨μκ° λ¦¬ν΄νλ κ°μ²΄ νλ‘νΌν°μ κ°μΌλ‘λ κΈ°μ‘΄ target κ°μ²΄ νλ‘νΌν°κ° κ°μ§ κ°μ μΈμλ‘ νλ μ½λ°± ν¨μ λλ νΉμ κ°μ λͺ μν μ μμ΅λλ€. μ½λ°± ν¨μμΌλλ κΈ°μ‘΄ target κ°μ²΄ νλ‘νΌν°μ κ°μ μ½λ°± ν¨μλ₯Ό μ μ©ν΄ κ°κ³΅ν κ°μΌλ‘ λ°λλ©°, νΉμ κ°μΌλλ targetμ κ°μ overwriteλ©λλ€.processPlan
κ°μ²΄ νλ‘νΌν°μ keyλ‘λ target objectμ μ΄λ―Έ μ‘΄μ¬νλ keyλ§ λͺ μν μ μμ΅λλ€. κ·Έλ μ§ μμ keyλ 무μν©λλ€.target object
μ μ΄λ―Έ μ‘΄μ¬νλ νλ‘νΌν°λ§ processν μ μκΈ° λλ¬Έμ κ°μ²΄μ μλ‘μ΄ νλ‘νΌν°λ₯Ό μΆκ°νκ³ μΆλ€λ©΄augment
λ©μλλ₯Ό μ¬μ©ν΄μΌ ν©λλ€.process
λ©μλμμ μ¬λ¬ κ°μ²΄ νλ‘νΌν°λ€μ λ³νλ μλ‘ λ 립μ μ λλ€.processPlan
κ°μ²΄μ κ°μΌλ‘ λκΈ΄ μ½λ°± ν¨μλ μ΄λ―Έ processλ κ°μ²΄μ νλ‘νΌν°λ₯Ό μ°Έμ‘°νμ§ μκ³ μλ target κ°μ²΄μ νλ‘νΌν°μ κ°λ§μ μ°Έμ‘°ν©λλ€.
const result = flattener({
userId: 12424,
userName: 'Max',
userAge: 25,
userProfile: {
userProfileText: 'My Name is Max.',
userFavoriteAnimal: 'zebra',
}
}).augment((target:Target) => {
const { userProfileText, userFavoriteAnimal } = target.userProfile
return {
isUserAdult: target.userAge > 19
userProfile: {
userIntroduce: `${userProfileText} My favorite animal is ${userFavoriteAnimal}.`,
}
},
}).returnResult()
/*
Result will be
{
userId: 10000
userName: 'Max',
userAge: 25,
isUserAdult: true,
userProfile: {
userProfileText: 'My Name is Max.',
userFavoriteAnimal: 'zebra',
userIntroduce: 'My name is Max. My favorite animal is zebra.',
}
}
*/
- target κ°μ²΄μ μλ‘μ΄ νλ‘νΌν°λ₯Ό μ½μ νμ¬ κ°μ²΄λ₯Ό μ¦κ°μν€λ λ©μλμ λλ€.
augmentPlan
νλΌλ―Έν°λ target objectλ₯Ό μΈμλ‘ λ°λ ν¨μμ λλ€. μ΄ ν¨μκ° λ¦¬ν΄νλ κ°μ²΄μ νλ‘νΌν°λ€μ target objectλ₯Ό μ°Έμ‘°ν μ μμ΅λλ€.augmentPlan
κ°μ²΄μ κ°μ ν¨μλ₯Ό κ°μ§ μ μμ΅λλ€. target κ°μ²΄μ μλ‘κ² μΆκ°λλ νλ‘νΌν°λ νΉμ κ°μ΄κ±°λ, target objectλ§μ μ°Έμ‘°νμ¬ λ§λ€μ΄μ ΈμΌ ν©λλ€.augmentPlan
κ°μ²΄μλ κΈ°μ‘΄target object
μλ μλ μλ‘μ΄ key κ°λ§ ν¬ν¨νλ κ²μ κΆμ₯ν©λλ€.augment()
λ©μλκ° μ¦κ°μ μ΄μ μ λ§μΆκ³ μμ΄μ κ·Έλ μ΅λλ€. plan κ°μ²΄μ κΈ°μ‘΄ keyκ°μ μ¬μ©ν΄ μλ‘μ΄ κ°μ λ겨주λ λ°©μμΌλ‘ κ°μ²΄λ₯Ό μμ ν μλ μμ§λ§, μ΄λ μ΄λ―Έ 'κ°κ³΅'μ μ’ λ μ΄μ μ΄ λ§μΆ°μ Έ μλprocess()
λ©μλμμλ κ°λ₯ν λμμ λλ€.
const result = flattener({
userId: 12424,
userName: 'Max',
userAge: 25,
userProfile: {
userProfileText: 'My Name is Max.',
userFavoriteAnimal: 'zebra',
userImage : {
mobile: '/image/12424/mobile',
desktop: '/image/12424/desktop',
}
}
}).remove([
'userId',
'userProfile.userProfileText',
'userProfile.userProfileImage.mobile',
]).returnResult()
/*
Result will be
{
userName: 'Max',
userAge: 25,
userProfile: {
userProfileText: 'My Name is Max.',
userFavoriteAnimal: 'zebra',
userImage : {
desktop: '/image/12424/desktop',
}
}
}
*/
- target κ°μ²΄μ νλ‘νΌν°λ₯Ό μμ νμ¬ κ°μ²΄λ₯Ό μΆμμν€λ λ©μλμ λλ€.
removePlan
μ λ¬Έμμ΄λ‘ μ΄λ£¨μ΄μ§ λ°°μ΄μ΄λ©°, λνΈ μ°μ°μ(.
)λ₯Ό ν΅ν΄ μ κ±°ν κ°μ²΄μ νλ‘νΌν°λ₯Ό ννν©λλ€.
- μ λ€λ₯Έ λ©μλλ€μ μμμμ λ³Ό μ μλ―,
returnResult()
λ©μλλFlattenTarget
μΈμ€ν΄μ€μμ λ³κ²½μ΄ λλtarget object
λ₯Ό λ°νν©λλ€. - μλ°μ€ν¬λ¦½νΈ λ³μμ λ³κ²½μ΄ μλ£λ κ°μ²΄λ₯Ό λ΄μ λλ κΌ μ΄ λ©μλλ₯Ό μ¬μ©ν΄
FlattenTarget
μΈμ€ν΄μ€μμ μΌλ° μλ°μ€ν¬λ¦½νΈ κ°μ²΄μΈtarget object
λ₯Ό λ°νν΄μΌ ν©λλ€. flatten()
ν¨μκ° κ°μ²΄μ μμ μ μμνλ μ§μ μ μ΄λΌλ©΄,returnResult()
λ μμ μ λ§λ¬΄λ¦¬νλ λ©μλμ λλ€.
import { flattener, Target } from 'functional-flattener'
const mockData = {
user_id: 12424,
user_name: 'max',
user_age: 25,
user_profile: {
user_profile_text: 'I Love Zebra',
user_favorite_animal: { id: 3, animal_name: 'vulture' },
userProfileImage: {
mobile: '/image/12424/mobile',
desktop: '/image/12424/desktop',
},
user_friends: [
{ id: 12324, name: 'julie', favorite_animal: { id: 0, animal_name: 'tiger' } },
{ id: 11424, name: 'michael', favorite_animal: { id: 1, animal_name: 'lion' } },
{ id: 18924, name: 'shawn', favorite_animal: { id: 2, animal_name: 'monkey' } },
],
},
}
const processPlan = (target:Target) => ({
userId: (id:number) => id + 10000,
userProfile: {
userProfileText: (text:string) => `Hello! My name is ${target.userName}. ${text}`,
userFriends: (friends:Friend[]) => friends.map(
(friend:Friend) => ({ ...friend, name: `${friend.name} the ${friend.favoriteAnimal.animalName}` }),
),
},
})
const augmentPlan = (target:Target) => ({
isRecentSignUser: target.userId > 10000,
isUserAdult: target.userAge > 19,
userProfile: {
userFriendsFavoriteAnimals: target.userProfile.userFriends.map(
(friend:Friend) => friend.favoriteAnimal.animalName,
),
},
})
const removePlan = [
'userId',
'userProfile.userProfileImage.mobile',
]
const changePlan = {
userAge: 'userCurrentAge',
'userProfile:userCurrentProfile': {
userProfileText: 'userIntroduce',
'userFavoriteAnimal:userAnimal': {
animalName: 'name',
},
},
}
const result = flattener(mockData).case({ to: 'camel' })
.process(processPlan)
.augment(augmentPlan)
.remove(removePlan)
.returnResult()
λ±ν λ°λΌμΌ νλ λ©μλ μ μ© μμλ μμ§λ§, flatten()
ν¨μλ‘ FlattenTarget
μΈμ€ν΄μ€λ₯Ό λ§λ€κ³ λ μ§νμ casing()
λ©μλλ₯Ό λ¨Όμ μ μ©νλ κ²μ μΆμ²ν©λλ€. ESLintλ₯Ό μ¬μ©νλ€λ©΄, μΆν μ μ©ν process()
νΉμ augment()
λ©μλμ μΈμλ‘ μ°μ΄λ plan
κ°μ²΄μ μΉ΄λ© μΌμ΄μ€κ° μλ λ€λ₯Έ μΌμ΄μ€μ key
λ₯Ό μ¬μ©νλ κ² λ§μΌλ‘λ μλ¬λ₯Ό λ°μμν€κΈ° λλ¬Έμ
λλ€.
const mockData = {
userFriends: [
{ id: 12324, name: 'julie', favoriteAnimal: { id: 0, animalName: 'tiger' } },
{ id: 11424, name: 'michael', favoriteAnimal: { id: 1, animalName: 'lion' } },
{ id: 18924, name: 'shawn', favoriteAnimal: { id: 2, animalName: 'monkey' } },
],
}
const processPlan = (target:Target) => ({
userFriends: (friends:Friend[]) => friends.map(
(friend:Friend) => ({ ...friend, name: `${friend.name} the ${friend.favoriteAnimal.animalName}` }),
)},
)
const result = flattener(mockData)
.process(processPlan)
.returnResult()
λ°°μ΄ μμ κ°μ²΄λ₯Ό μμ νκ³ μΆμ λλ λ°°μ΄μ μμ νλ μ½λ°± ν¨μλ₯Ό λκΈ°λ μμ ν μ λ°°μ΄μ λ°ννλ map
μ΄λ filter
μ κ°μ λ©μλλ₯Ό μ¬μ©νλ κ²μ κΆμ₯ν©λλ€.
- Add
FlattenTarget.prototype.modify()
: This method can modify target object according to modify plan.Modify()
will be a superset method of process, changeKey, augment and remove method. It will be going to operate those method`s modification all at once. - Add
README.md
written in English - Add More Detail error handling
- Add More test cases
- Anyone can open a
pull request
orissues
. Just ensure passing every existing tests suites in./lib/test dir
. - There is
github action
to verify that all test suites are passed whenpull reqeust
is opened. - MIT License