Aggregate 메서드
C#의 Linq는 집계 함수를 제공합니다. 그중 Aggregate() 메서드는 누적 연산을 수행합니다.
메서드 오버로드
System.Linq의 Enumerable 클래스에 정의된 Aggregate() 메서드를 확인할 수 있으며, 세 가지 오버로드된 버전이 존재합니다.
public static TSource Aggregate<TSource>(
this IEnumerable<TSource> source,
Func<TSource, TSource, TSource> func);
public static TAccumulate Aggregate<TSource, TAccumulate>(
this IEnumerable<TSource> source,
TAccumulate seed,
Func<TAccumulate, TSource, TAccumulate> func);
public static TResult Aggregate<TSource, TAccumulate, TResult>(
this IEnumerable<TSource> source,
TAccumulate seed,
Func<TAccumulate, TSource, TAccumulate> func,
Func<TAccumulate, TResult> resultSelector);
Aggregate() 메서드 동작 과정을 글로 표현하기 어려우므로 아래 예제들을 통해 Aggregate() 메서드의 동작 과정을 살펴봅시다.
예제 1. string 타입의 List
다음 예제는 string 타입의 List에서 Aggregate() 메서드를 호출합니다.
class Program
{
static void Main(string[] args)
{
List<string> strList = new List<string>()
{
"Java", "C#", "React", "Svelte"
};
string linqMethodResult = strList.Aggregate((s1, s2) => {
Console.WriteLine("s1: " + s1);
Console.WriteLine("s2: " + s2 + "\n");
return s1 + " / " + s2;
});
Console.WriteLine("메서드 구문 결과: " + linqMethodResult);
}
}
[실행 결과]
s1: Java
s2: C#
s1: Java / C#
s2: React
s1: Java / C# / React
s2: Svelte
메서드 구문 결과: Java / C# / React / Svelte
동작 과정
Aggregate() 메서드에는 다음 람다식이 작성되어 있습니다.
string linqMethodResult = strList.Aggregate((s1, s2) => {
Console.WriteLine("s1: " + s1);
Console.WriteLine("s2: " + s2 + "\n");
return s1 + " / " + s2;
});
여기서 s1은 컬렉션을 순회하면서 람다식에서 반환되는 값이 누적됩니다. Aggregate() 함수는 문자열 " / "로 구분된 문자열을 반환하며, 다음 내용은 Aggregate() 함수 동작 과정을 단계별로 설명합니다.
1단계: 첫 번째 루프에서 s1은 strList의 첫 번째 요소인 "Java"이며, s2는 다음 요소(두 번째 요소)인 "C#"입니다. 람다식에서 반환된 값은 s1에 누적됩니다.
2단계: 1단계 람다식에서 반환된 값이 s1에 누적된 것을 확인할 수 있으며, s2는 세 번째 요소인 "React"입니다. 람다식에서 반환된 값은 s1에 누적됩니다.
3단계: 2단계 람다식에서 반환된 값이 s1에 누적된 것을 확인할 수 있으며, s2는 네 번째 요소인 "Svelte"입니다. 람다식에서 반환된 값은 s1에 누적됩니다.
4단계: s2가 가리키는 요소가 없으므로 Aggregate() 메서드는 누적된 결과인 s1을 반환합니다.
예제 2. int 타입의 List
다음 예제는 int 타입의 List에서 Aggregate() 메서드를 사용하여 모든 요소를 곱한 결과를 구합니다.
class Program
{
static void Main(string[] args)
{
List<int> intList = new List<int>()
{
5, 10, 20
};
int linqMethodResult = intList.Aggregate((num1, num2) => {
Console.WriteLine("num1: " + num1);
Console.WriteLine("num2: " + num2 + "\n");
return num1 * num2;
});
Console.WriteLine("메서드 구문 결과: " + linqMethodResult);
}
}
[실행 결과]
num1: 5
num2: 10
num1: 50
num2: 20
메서드 구문 결과: 1000
동작 과정
1단계: 5 * 10 결과를 num1에 할당합니다.
2단계: 1단계의 결과인 50에 20을 곱한 결과를 num1에 할당합니다.
3단계: Aggregate() 메서드는 2단계의 결과인 1000을 반환합니다.
예제 3. seed 매개변수가 있는 Aggregate 메서드
Aggregate() 메서드의 두 번째 오버로드된 버전은 seed 매개변수가 존재한다는 것입니다. 쉽게 이야기하자면, 처음 루프에서 누적되는 값이 데이터 집합의 첫 번째 요소가 아닌 seed 값을 가리킵니다.
다음 예제는 seed의 값을 3으로 설정하고 int 타입의 List에서 Aggregate() 메서드를 사용하여 모든 요소를 곱한 결과를 구합니다.
class Program
{
static void Main(string[] args)
{
List<int> intList = new List<int>()
{
5, 10, 20
};
int linqMethodResult = intList.Aggregate(3, (num1, num2) => {
Console.WriteLine("num1: " + num1);
Console.WriteLine("num2: " + num2 + "\n");
return num1 * num2;
});
Console.WriteLine("메서드 구문 결과: " + linqMethodResult);
}
}
[실행 결과]
num1: 3
num2: 5
num1: 15
num2: 10
num1: 150
num2: 20
메서드 구문 결과: 3000
동작 과정
1단계: num1은 intList의 첫 번째 요소가 아닌 seed값 3입니다. num2는 두 번째 요소가 아닌 첫 번째 요소 5입니다. 3 * 5 결과를 num1에 할당합니다.
2단계: num1은 1단계의 결과인 15이며, num2는 두 번째 요소인 10입니다. 15 * 10 결과를 num1에 할당합니다.
3단계: num1은 2단계의 결과인 150이며, num2는 세 번째 요소인 20입니다. 150 * 20 결과를 num1에 할당합니다.
4단계: Aggregate() 메서드는 3단계의 결과인 3000을 반환합니다.
예제 4. 사용자 정의 클래스 - seed값
다음 예제는 사용자 정의 클래스인 Person 타입의 List에서 Aggregate() 메서드를 사용하여 모든 사람들의 이름을 하나의 문자열로 연결합니다.
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public int Money { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Person> personA = new List<Person>
{
new Person{Name ="Bob", Age = 20, Money = 30000},
new Person{Name ="Nick", Age = 30, Money = 50000},
new Person{Name ="Tom", Age = 40, Money = 80000}
};
// 1. 질의 구문(Query Syntax)
string linqQueryResult = (from person in personA
select person)
.Aggregate<Person, string>("Name: ", (person1, person2) => {
return person1 + person2.Name + " / ";
});
// 2. 메서드 구문(Method Syntax)
string linqMethodResult = personA
.Aggregate<Person, string>("Name: ", (person1, person2) => {
return person1 + person2.Name + " / ";
});
// 맨 마지막 문자열 " / "을 제거하는 작업
int queryResultLastIndex = linqQueryResult.LastIndexOf("/");
linqQueryResult = linqQueryResult.Remove(queryResultLastIndex);
int methodResultLastIndex = linqMethodResult.LastIndexOf("/");
linqMethodResult = linqMethodResult.Remove(methodResultLastIndex);
Console.WriteLine("질의 구문");
Console.WriteLine("질의 구문 결과: " + linqQueryResult);
Console.WriteLine("\n메서드 구문");
Console.WriteLine("메서드 구문 결과: " + linqMethodResult);
}
}
[실행 결과]
질의 구문
질의 구문 결과: Name: Bob / Nick / Tom
메서드 구문
메서드 구문 결과: Name: Bob / Nick / Tom
위 예제는 seed를 가지는 Aggregate() 메서드를 사용했으며, 꺾새(<>) 내부에 클래스 타입과 반환 타입을 명시했다는 점을 유의해주세요.
예제 5. resultSelector 매개변수가 있는 Aggregate 메서드
resultSelector은 Aggregate() 메서드의 세 번째 오버로드된 버전에 존재하며, 최종 누적 결과에 대해 처리하고자 하는 로직을 작성할 수 있습니다.
다음 예제는 예제 4. 사용자 정의 클래스 - seed값에서 마지막 문자열 " / "을 제거하는 로직을 resultSelector을 사용하여 구현합니다.
Func 대리자 꺾새(<>) 내부에 resultSelector의 타입이 추가된 점을 유의해주세요.
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public int Money { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Person> personA = new List<Person>
{
new Person{Name ="Bob", Age = 20, Money = 30000},
new Person{Name ="Nick", Age = 30, Money = 50000},
new Person{Name ="Tom", Age = 40, Money = 80000}
};
// 1. 질의 구문(Query Syntax)
string linqQueryResult = (from person in personA
select person)
.Aggregate<Person, string, string>(
"Name: ", // seed값
(person1, person2) => { // 누적기 함수
return person1 + person2.Name + " / ";
},
(person1) => { // 누적된 최종 결과에 대해 처리하고자하는 로직
int LastIndex = person1.LastIndexOf("/");
return person1.Remove(LastIndex);
});
// 2. 메서드 구문(Method Syntax)
string linqMethodResult = personA
.Aggregate<Person, string, string>(
"Name: ", // seed값
(person1, person2) => { // 누적기 함수
return person1 + person2.Name + " / ";
},
(person1) => { // 누적된 최종 결과에 대해 처리하고자하는 로직
int LastIndex = person1.LastIndexOf("/");
return person1.Remove(LastIndex);
});
Console.WriteLine("질의 구문");
Console.WriteLine("질의 구문 결과: " + linqQueryResult);
Console.WriteLine("\n메서드 구문");
Console.WriteLine("메서드 구문 결과: " + linqMethodResult);
}
}
[실행 결과]
질의 구문
질의 구문 결과: Name: Bob / Nick / Tom
메서드 구문
메서드 구문 결과: Name: Bob / Nick / Tom
'C# > LINQ' 카테고리의 다른 글
[C#]LINQ 내부 조인(Inner Join) - Join 메서드 (0) | 2022.08.06 |
---|---|
[C#]LINQ 데이터 그룹화 - GroupBy 메서드 (1) | 2022.08.04 |
[C#]LINQ 데이터 개수 구하기 - Count 메서드 (0) | 2022.07.31 |
[C#]LINQ 평균 구하기 - Average 메서드 (0) | 2022.07.31 |
[C#]LINQ 최소값, 최대값 구하기 - Min, Max 메서드 (0) | 2022.07.31 |
댓글