Dart – Functional Programming

형 변환

void main(){
  
  //List to Map / Set
  List<String> blackPink = ['로제','지수','리사','제니','제니'];
  print(blackPink.asMap()); //{0: 로제, 1: 지수, 2: 리사, 3: 제니}
  print(blackPink.toSet()); // {로제, 지수, 리사, 제니} >> 중복 제거됨
  
  
  // Map to List
  Map blackPinkMap = blackPink.asMap();
  print(blackPinkMap.keys); // (0, 1, 2, 3, 4) iterable 이므로 주로 list로 변환하여 처리
  print(blackPinkMap.keys.toList()); //[0, 1, 2, 3, 4]
  print(blackPinkMap.values.toList()); //[로제, 지수, 리사, 제니, 제니]

  
  // List to Set
  Set blackPinkSet = Set.from(blackPink);
  print(blackPinkSet); //{로제, 지수, 리사, 제니}
  
  // Set to List
  print(blackPinkSet.toList()); //[로제, 지수, 리사, 제니]
}

List.map()

void main() {
  List<String> blackPink = ['로제', '지수', '리사', '제니'];

  final newBlackPink = blackPink.map((x) {
    return '블랙핑크 $x';
  }); // map 을 사용하면 새로운 iterable 형태로 리턴됨
  
  print(newBlackPink); //(블랙핑크 로제, 블랙핑크 지수, 블랙핑크 리사, 블랙핑크 제니) iterable 형태로 반환됨
  print(newBlackPink.toList()); //[블랙핑크 로제, 블랙핑크 지수, 블랙핑크 리사, 블랙핑크 제니]  List로 변환
  
  final newBlackPink2 = blackPink.map((x)=> '블랙핑크 $x'); // arrow func으로 사용가능
  print(newBlackPink2.toList()); // 데이터 쉬운 가공을 위해 list로 변환
  
  print(newBlackPink == newBlackPink2); // false
  
  //[1.jpg,3.jpg,5.jpg,7.jpg,9.jpg] 로 가공 예제
  String number = '13579';
  final parsed = number.split('').map((x)=>'${x}.jpg').toList();
  print(parsed);
}

Map.map()

void main() {
  Map<String, String> harryPotter = {
    'Harry Potter': '해리포터',
    'Ron Weasley': '론 위즐리',
    'Hermione Granger': '헤르미온느 그레인저',
  };

  /*
   * Map을 map()할때는 key,value를 모두 파라미터로 전달받는다.
   * */
  final result = harryPotter.map(
    (key, value) => MapEntry(
      'Harry Potter Character $key',
      '해리포터 캐릭터 $value',
    ),
  );
  
  print(result); // {Harry Potter Character Harry Potter: 해리포터 캐릭터 해리포터, Harry Potter Character Ron Weasley: 해리포터 캐릭터 론 위즐리, Harry Potter Character Hermione Granger: 해리포터 캐릭터 헤르미온느 그레인저}
  
  // Map의 key 나 values 값들을 변경하고싶을때
  final keys = harryPotter.keys.map((x)=>'HPC $x').toList(); //[HPC Harry Potter, HPC Ron Weasley, HPC Hermione Granger]
  final values = harryPotter.values.map((x)=>'해리포터 $x').toList(); // [해리포터 해리포터, 해리포터 론 위즐리, 해리포터 헤르미온느 그레인저]
  
}

Set.map()

void main() {
  Set blackPinkSet = {
    '로제','지수','제니','리사'
  };
  
  // Set > Set
  final newSet = blackPinkSet.map((x)=>'블랙핑크 $x').toSet(); //{블랙핑크 로제, 블랙핑크 지수, 블랙핑크 제니, 블랙핑크 리사}

}

where

void main() {
  List<Map<String,String>> people = [
    {
      'name' : '로제',
      'group' : '블랙핑크',
    },
    {
      'name' : '지수',
      'group' : '블랙핑크',
    },
    {
      'name' : 'RM',
      'group' : 'BTS',
    },
    {
      'name' : '뷔',
      'group' : 'BTS',
    }
  ];
  
  // where 은 true 혹은 false를 리턴
  final blackPink = people.where((x)=>x['group'] == '블랙핑크').toList(); //[{name: 로제, group: 블랙핑크}, {name: 지수, group: 블랙핑크}]
  final bts = people.where((x)=>x['group'] == 'BTS').toList(); // [{name: RM, group: BTS}, {name: 뷔, group: BTS}]

}

reduce()

void main() {
  List<int> numbers = [1,3,5,7,9];
  final result = numbers.reduce((prev,next){
    print('-----------------');
    print('previous : $prev');
    print('next: $next');
    print('total: ${prev + next}');
    
    return prev + next;
  });
  
  print(result); // 25
  
  
  List<String> words = [
    '안녕하세요 ',
    '저는',
    ' Dylan 입니다'
  ];
    
  final sentence = words.reduce((prev,next)=>prev+next);
  print(sentence); //안녕하세요 저는 Dylan 입니다
  
  /*
   * 기존 List words는 String 으로 선언되었는데 return을 int로 하면 에러 발생
   * reduce는 선언된 타입 그대로 리턴해야함
   * */
  words.reduce((prev,next)=> prev.length + next.length); // error
  
}

fold() – reduce와 비슷하나 return 값을 지정가능

void main() {
  List<int> numbers = [1, 3, 5, 7, 9];

  /*
   * reduce 는 동일한 타입을 리턴 해야하지만, fold 는 리턴할 타입을 직접 지정할 수 있음
   * fold prev에 첫번째 제공한 파라미터가 들어감(아래코드에서는 0)
   * */
  final sum = numbers.fold<int>(0, (prev, next) => prev + next);
  print(sum); //25

  List<String> words = [
    '안녕하세요 ? ',
    '저는 ',
    'Dylan 입니다.',
  ];

  final sentence = words.fold<String>('', (prev, next) => prev + next);
  print(sentence); //안녕하세요 ? 저는 Dylan 입니다.

  
  /*
   * words의 모든 글자의 길이 세기
   * 기존 reduce로 불가능한 것이 fold로는 가능
   * */
  final count = words.fold<int>(0, (prev, next) => prev + next.length);
  print(count); // 21
}

cascading operator

void main() {
  List<int> even = [2, 4, 6, 8];
  List<int> odd = [1, 3, 5, 7];
  
  print([even,odd]); //[[2, 4, 6, 8], [1, 3, 5, 7]]
  
  /*
   * cascading operator
   * 값을 펼쳐서 넣을때
   * 새로운 리스트로 반환하므로 기존 even 리스트와 다름
   * */
  print([...even,...odd]); //[2, 4, 6, 8, 1, 3, 5, 7]
  print(even == [...even]); // false
}

유용한 팁

void main() {
  /*
   * 다른 서버로부터 들어온 데이터를 키의 스펠링이 맞는지 틀렸는지 등 100% 신뢰할 수 없음.
   * 그래서 일반적으로 미리 class를 정의해두고 instance로 구조화 하여 사용
   * 아래 people 데이터가 서버로 부터 들어온 데이터라고 가정
   * */
  List<Map<String, String>> people = [
    {
      'name': '로제',
      'group': '블랙핑크',
    },
    {
      'name': '지수',
      'group': '블랙핑크',
    },
    {
      'name': 'RM',
      'group': 'BTS',
    },
    {
      'name': '뷔',
      'group': 'BTS',
    }
  ];

  // 데이터를 인스턴스로 가공
  final parsedPeople = people
      .map((x) => Person(
            name: x['name']!,
            group: x['group']!,
          ))
      .toList();

  print(
      parsedPeople); //[Instance of 'Person', Instance of 'Person', Instance of 'Person', Instance of 'Person']

  for (Person person in parsedPeople) {
    print(person.name);
    print(person.group);
  }

  final bts = parsedPeople.where((x) => x.group == 'BTS');
  print(bts); //(Person(name:RM,group:BTS), Person(name:뷔,group:BTS))

  // 새로운 값을 리턴해주므로 한번에 체이닝도 가능
  final result = people
      .map((x) => Person(
            name: x['name']!,
            group: x['group']!,
          ))
      .where((x) => x.group == 'BTS')
      .fold<int>(0, (prev, next) => prev + next.name.length);
  
  print(result); //3
}

class Person {
  final String name;
  final String group;

  Person({
    required this.name,
    required this.group,
  });
}