본문 바로가기
Utils

C#, 확장함수를 이용하여 Collection<T> First, Last 가져오기

by Client. DJ 2021. 12. 19.
반응형

스크립트를 작성하다보면 아래와 같은 상황이 자주 연출됩니다.

private static void Function()
{
	List<int> list = new List<int>();
	list.Add(0);
	list.Add(1);
	list.Add(2);
	list.Add(3);
	list.Add(4);

	var first = list[0];
	var last = list[list.Count - 1];
}

코드에는 아무런 문제가 없지만, 가독성에 있어서 적합하지 않습니다. 또한 위의 코드처럼 특정 Collection뿐만이 아니라 Collection마다 First, Last를 가져오는 방식은 여러가지입니다.

 

private static void Function()
{
	Dictionary<int, string> dic = new Dictionary<int, string>();
	dic.Add(1, "A");
	dic.Add(0, "B");
	dic.Add(4, "C");
	dic.Add(3, "D");
	dic.Add(2, "E");

	var enumerator = dic.GetEnumerator();
	KeyValuePair<int, string> first, last;
	while (enumerator.MoveNext())
	{
		// ...?
	}
}

Dictionary의 경우는 방법은 여러가지이지만 코드가 더욱 복잡해집니다. 물론 Dictionary 사용 용도에는 안 맞을 수 있지만, 코드를 사용하다보면 처음과 끝이 필요할 때가 더러 있습니다.

 

아래와 같이 모든 컬렉션에 처음과 마지막을 깔끔하게 가져오는 확장 함수를 만들어서 사용할 수 있습니다.

public static class CollectionUtils
{
    /// <summary>
    /// 컬렉션 첫번째 값 가져오기
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="collection"></param>
    /// <returns></returns>
    public static T GetFirst<T>(this ICollection<T> collection)
    {
        if (collection.Count > 0)
        {
            var enumerator = collection.GetEnumerator();
            enumerator.MoveNext();
            return enumerator.Current;
        }
        return default(T);
    }

    /// <summary>
    /// 컬렉션 마지막 값 가져오기
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="collection"></param>
    /// <returns></returns>
    public static T GetLast<T>(this ICollection<T> collection)
    {
        if (collection.Count > 0)
        {
            var list = collection as List<T>;
            if (list != null)
            {
                return list[list.Count - 1];
            }

            var array = collection as Array;
            if (array != null)
            {
                return (T)array.GetValue(array.Length - 1);
            }

            T item = default(T);
            var enumerator = collection.GetEnumerator();
            while (enumerator.MoveNext())
            {
                item = enumerator.Current;
            }
            return item;
        }
        return default(T);
    }

    /// <summary>
    /// 이전 값 가져오기
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="collection"></param>
    /// <param name="pivot"></param>
    /// <returns></returns>
    public static T GetPrevious<T>(this ICollection<T> collection, Predicate<T> match)
    {
        if (collection.Count > 0)
        {
            var enumerator = collection.GetEnumerator();
            T previous = collection.GetLast();
            while (enumerator.MoveNext())
            {
                if (match(enumerator.Current))
                    return previous;
                previous = enumerator.Current;
            }
        }
        return collection.GetFirst();
    }

    /// <summary>
    /// 다음 값 가져오기
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="collection"></param>
    /// <param name="pivot"></param>
    /// <returns></returns>
    public static T GetNext<T>(this ICollection<T> collection, Predicate<T> match)
    {
        if (collection.Count > 0)
        {
            var enumerator = collection.GetEnumerator();
            while (enumerator.MoveNext())
            {
                if (match(enumerator.Current))
                {
                    if (!enumerator.MoveNext())
                        return collection.GetFirst();
                    else
                        return enumerator.Current;
                }
            }
        }
        return collection.GetFirst();
    }
    

    /// <summary>
    /// 삭제
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="collection"></param>
    /// <param name="match"></param>
    /// <returns></returns>
    public static bool Remove<T>(this ICollection<T> collection, Predicate<T> match)
    {
        if (collection.Count > 0)
        {
            T item = default;
            var enumerator = collection.GetEnumerator();
            while (enumerator.MoveNext())
            {
                if (match(enumerator.Current))
                {
                    item = enumerator.Current;
                    break;
                }
            }
            if (item != default)
            {
                collection.Remove(item);
                return true;
            }
        }
        return false;
    }
}

 

해당 코드는 아래처럼 사용할 수 있습니다.

private static void Function()
{
	Dictionary<int, string> dic = new Dictionary<int, string>();
	dic.Add(1, "A");
	dic.Add(0, "B");
	dic.Add(4, "C");
	dic.Add(3, "D");
	dic.Add(2, "E");

	//var enumerator = dic.GetEnumerator();
	//KeyValuePair<int, string> first, last;
	//while (enumerator.MoveNext())
	//{
	//    // ...?
	//}

	var first = dic.GetFirst();
	var last = dic.GetLast();
}

 

추가)

GetNext(), GetPrevious()는 해당되는 item에서 앞, 뒤 값을 가져옵니다. 개인적으로 리스트를 순환식으로 호출해야하는 작업이 있어서 작성하게된 함수입니다. 해당 함수는 반복해서 호출하면 순환식으로 쓸 수가 있습니다.

private static void Function()
{
	List<int> list = new List<int>();
	list.Add(0);
	list.Add(1);
	list.Add(2);
	list.Add(3);
	list.Add(4);

	var prev = list.GetPrevious(item => item == 2); // result => 1
	var next = list.GetNext(item => item == 2); 	// result => 3
}

 

반응형

댓글