r
예를 들어, Category 속성을 가진 Item 클래스의 리스트가 있을 때, Category 값이 변경되는 지점의 인덱스를 찾는 방법은 다음과 같습니다:
using System;
using System.Collections.Generic;
using System.Linq;
public class Item
{
public string Category { get; set; }
public string Name { get; set; }
}
public class Program
{
public static void Main()
{
List<Item> items = new List<Item>
{
new Item { Category = "A", Name = "Apple" },
new Item { Category = "A", Name = "Avocado" },
new Item { Category = "B", Name = "Banana" },
new Item { Category = "B", Name = "Blueberry" },
new Item { Category = "C", Name = "Cherry" },
};
var sortedItems = items.OrderBy(x => x.Category).ToList();
var changeIndices = sortedItems
.Select((item, index) => new { Item = item, Index = index })
.Where(x => x.Index == 0 || x.Item.Category != sortedItems[x.Index - 1].Category)
.Select(x => x.Index)
.ToList();
foreach (var index in changeIndices)
{
Console.WriteLine($"Change at index {index}, Category: {sortedItems[index].Category}");
}
}
}
코드 설명:
1. 정렬: items 리스트를 Category 기준으로 정렬하여 sortedItems 리스트를 생성합니다.
2. 인덱스와 항목 매핑: Select 메서드를 사용하여 각 항목과 해당 인덱스를 새로운 익명 객체로 매핑합니다.
3. 조건 필터링: Where 메서드를 사용하여 다음 조건을 만족하는 항목만 필터링합니다:
첫 번째 항목인 경우 (x.Index == 0)
현재 항목의 Category가 이전 항목과 다른 경우 (x.Item.Category != sortedItems[x.Index - 1].Category)
4. 인덱스 추출: 필터링된 결과에서 Select 메서드를 사용하여 인덱스만 추출합니다.
5. 결과 출력: 각 변경 지점의 인덱스와 해당 Category를 출력합니다.
출력 결과:
Change at index 0, Category: A
Change at index 2, Category: B
Change at index 4, Category: C
좋습니다! 컬럼명을 문자열로 지정하고 싶다면, 런타임에 해당 속성 값을 읽기 위해 Reflection을 써야 합니다. 아래는 LINQ + Reflection + 고급 문법을 조합한 방법입니다.
---
문자열 기준 속성으로 그룹 변경 인덱스 구하기
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
public class Item
{
public string Category { get; set; }
public string Name { get; set; }
}
public static class LinqExtensions
{
public static List<int> GetChangeIndicesByProperty<T>(this IEnumerable<T> source, string propertyName)
{
var prop = typeof(T).GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
if (prop == null)
throw new ArgumentException($"Property '{propertyName}' not found on type '{typeof(T).Name}'");
// 정렬 후 리스트화
var sorted = source.OrderBy(x => prop.GetValue(x, null)).ToList();
// 변경 지점 인덱스 계산
return sorted
.Select((item, index) => new { Value = prop.GetValue(item, null), Index = index })
.Where(x => x.Index == 0 || !object.Equals(x.Value, prop.GetValue(sorted[x.Index - 1], null)))
.Select(x => x.Index)
.ToList();
}
}
public class Program
{
public static void Main()
{
var items = new List<Item>
{
new Item { Category = "B", Name = "Banana" },
new Item { Category = "A", Name = "Apple" },
new Item { Category = "A", Name = "Avocado" },
new Item { Category = "C", Name = "Cherry" },
new Item { Category = "B", Name = "Blueberry" }
};
string sortBy = "Category"; // 또는 "Name"
var changeIndices = items.GetChangeIndicesByProperty(sortBy);
var sorted = items.OrderBy(x => typeof(Item).GetProperty(sortBy).GetValue(x)).ToList();
foreach (var index in changeIndices)
{
var value = typeof(Item).GetProperty(sortBy).GetValue(sorted[index]);
Console.WriteLine($"Change at index {index}, {sortBy}: {value}");
}
}
}
---
출력 (Category 기준일 경우):
Change at index 0, Category: A
Change at index 2, Category: B
Change at index 4, Category: C
---
이 코드는 원하는 컬럼명을 문자열로 받아, 해당 컬럼 기준으로 정렬하고, 값이 변하는 인덱스를 반환합니다. 매우 동적으로 설계되어 다양한 객체에 적용할 수 있어요.
혹시 컬럼이 여러 개일 경우(복합 정렬)도 고려하셔야 하나요?
좋습니다! List<dynamic> 자료형을 사용하는 경우는 주로 익명 객체나 ExpandoObject 등을 쓸 때인데, 이럴 경우에도 문자열로 지정한 컬럼명 기준으로 정렬하고 값이 변하는 인덱스를 LINQ + Reflection을 활용해 얻을 수 있습니다.
아래는 List<dynamic>에 대응하는 코드입니다:
---
예제 코드 (List<dynamic> + 컬럼명 문자열 기준):
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
public static class DynamicListExtensions
{
public static List<int> GetChangeIndicesByProperty(this List<dynamic> source, string propertyName)
{
// 정렬
var sorted = source.OrderBy(x => GetPropertyValue(x, propertyName)).ToList();
// 변경 지점 찾기
return sorted
.Select((item, index) => new { Value = GetPropertyValue(item, propertyName), Index = index })
.Where(x => x.Index == 0 || !object.Equals(
x.Value, GetPropertyValue(sorted[x.Index - 1], propertyName)))
.Select(x => x.Index)
.ToList();
}
private static object GetPropertyValue(dynamic obj, string propertyName)
{
if (obj is ExpandoObject)
{
var dict = (IDictionary<string, object>)obj;
return dict.ContainsKey(propertyName) ? dict[propertyName] : null;
}
var prop = obj.GetType().GetProperty(propertyName);
return prop?.GetValue(obj, null);
}
}
public class Program
{
public static void Main()
{
dynamic item1 = new ExpandoObject();
item1.Category = "B";
item1.Name = "Banana";
dynamic item2 = new ExpandoObject();
item2.Category = "A";
item2.Name = "Apple";
dynamic item3 = new ExpandoObject();
item3.Category = "A";
item3.Name = "Avocado";
dynamic item4 = new ExpandoObject();
item4.Category = "C";
item4.Name = "Cherry";
dynamic item5 = new ExpandoObject();
item5.Category = "B";
item5.Name = "Blueberry";
List<dynamic> items = new List<dynamic> { item1, item2, item3, item4, item5 };
string sortBy = "Category";
var changeIndices = items.GetChangeIndicesByProperty(sortBy);
var sorted = items.OrderBy(x => DynamicListExtensions.GetPropertyValue(x, sortBy)).ToList();
foreach (var index in changeIndices)
{
Console.WriteLine($"Change at index {index}, {sortBy}: {DynamicListExtensions.GetPropertyValue(sorted[index], sortBy)}");
}
}
}
---
출력:
Change at index 0, Category: A
Change at index 2, Category: B
Change at index 4, Category: C
---
이 방식은 ExpandoObject와 익명 타입(dynamic) 모두에 대응합니다. 만약 JSON 파싱 결과라면 ExpandoObject 구조일 가능성이 높기 때문에 이 코드 그대로 활용하셔도 됩니다.
필요하시면 GroupBy 등으로 확장하거나 복합 조건 정렬도 도와드릴 수 있어요. 원하시는 방향이 있을까요?
r