[C#] 중복된 Key를 허용하며, Key로 정렬 된 해쉬맵(Dictionary)

C#의 자료구조 중 Key-Value 컨테이너는 Dictionary입니다. 경우에 따라 Key로 정렬된 Dictionary는 SortedDictionary 클래스로써 C#에서 기본적으로 제공하고 있습니다. 그런데, Dictionary이므로 Key가 중복될 수 없습니다. 중복된 값을 정렬해서 저장하기 위해서는 Set 자료 구조를 이용해야 합니다. 그런데, Set는 Key만을 저장할 수 있고 Value는 저장할 수 없습니다. 중복된 Key를 허용하면서, Key로 정렬된 컨테이너는 기본적으로 C#에서 제공하지 않습니다. 하지만 두개의 클래스를 조합하면 가능한데, SortedSet과 Tuple입니다. 이 두개의 클래스를 통해 중복된 Key를 허용하면서, Key로 정렬된 컨테이너에 대한 클래스는 아래와 같습니다.

public class SortedTupleBag<TKey, TValue> : SortedSet<Tuple<TKey, TValue>>
where TKey : IComparable
{
    private class TupleComparer : Comparer<Tuple<TKey, TValue>>
    {
        public override int Compare(Tuple<TKey, TValue> x, Tuple<TKey, TValue> y)
        {
            if (x == null || y == null) return 0;

            return x.Item1.Equals(y.Item1) ? 1 : Comparer<TKey>.Default.Compare(x.Item1, y.Item1);
        }
    }

    public SortedTupleBag() : base(new TupleComparer()) { }

    public void Add(TKey key, TValue value)
    {
        Add(new Tuple<TKey, TValue>(key, value));
    }
}

이 클래스의 활용 예는 아래의 코드와 같습니다.

var tuples = new SortedTupleBag<double, double>();

tuples.Add(10.0, 100.1);
tuples.Add(40.0, 100.4);
tuples.Add(10.0, 1000.1);
tuples.Add(80.0, 20.8);
tuples.Add(70.0, 2100.7);
tuples.Add(50.0, 200.5);

Add 함수의 첫번째는 Key이고 두번째는 Value입니다. Key의 순서대로 정렬되어 있는지 확인하기 위해 아래의 코드를 통해 살펴볼 수 있습니다.

Console.WriteLine("Sorted List");
foreach (var tuple in tuples)
{
    Console.WriteLine("{0} {1}", tuple.Item1, tuple.Item2);
}

Console.WriteLine("");

Console.WriteLine("Min: {0}, Max: {1}", tuples.Min, tuples.Max);

위 코드의 실행 결과는 아래와 같습니다.

Sorted List
10 100.1
10 1000.1
40 100.4
50 200.5
70 2100.7
80 20.8

Min: (10, 100.1), Max: (80, 20.8)

Visual Studio Code의 Python에서 코드 자동 완성 기능이 느리게 작동할 때

필자는 파이션이나 Go와 같은 언어를 위한 IDE 툴로써 Visual Studio Code를 사용합니다. 파이썬에서 코드 자동 완성 기능이 느리게 작동할때, 위의 이미지에서 보이는 Jedi Enable를 비활성화하여 IntelliSense 엔진을 Jedi가 아닌 Microsoft Python Analysis Engine을 사용하도록 지정하면 해결됩니다. 이는 기본설정이 아니므로 무엇가 부작용이 있을 수도 있다는 점을 참고하기 바랍니다.

위치기반 CCTV 관리 솔루션의 경로 검색

위치기반 CCTV 관리 솔루션은 GIS를 활용하여 CCTV를 효과적으로 관리하고 활용할 수 있는 시스템입니다. CCTV를 검색할 때, 위치의 관점에서 검색할 수 있는데.. 공간 검색 기능 중에 하나가 “경로 검색”입니다. 사용자가 지도 상에서 경로를 지정하면, 해당 경로에 일정한 버퍼 영역을 형성하고, 형성된 버퍼에 포함되는 CCTV를 검색해 주는 기능입니다. 아래의 동영상은 이러한 기능에 대한 소개 동영상입니다.

GIS 기반의 CCTV 시스템에서는 지도 위에 여러 개의 CCTV 영상을 동시에 표시하게 되는데, 이때 CCTV 영상들이 서로 겹치게 되어 사용자를 혼란스럽게 만듭니다. 이런 경우 지도 위의 CCTV 영상의 위치를 사용자가 변경할 수 있도록 유도하는 기능을 제공합니다. (참고: 위의 동영상에서 CCTV 영상은 실제 위치와 아무런 관련이 없습니다.)

Android v6.0(API Level 23) 이상의 Permission 관련 API

Android 6.0에서는 이전 버전과 다르게 개발자가 지정한 Permission에 대해서 사용자가 다시 한번 허용할 것인지를 묻는 과정을 요구한다. 즉, 6.0 이전에서는 개발자가 지정한 퍼미션에 대해 설치시에 사용자에게 알리고 설치가 되면 사용자는 설치된 앱을 사용하게 되지만, 6.0 이상에서는 설치 뿐만 아니라 실제 실행시에 사용자의 퍼미션에 대한 허용 여부를 UI를 통해 명시적으로 지정해줘야 한다.

예를들어 아래처럼 안드로이드의 버전에 상관없이 퍼미션은 AndroidManifest.xml에 지정된다.


안드로이드 6.0 이전은 이게 전부였다. 그러나 6.0 이후부터는 위의 개발자가 지정한 퍼미션 뿐만 아니라 사용자도 퍼미션의 허용 여부를 지정해야 한다. 이를 위해 개발자는 추가적인 코드가 필요하다. 먼저 특정한 퍼미션을 필요로 하는 API를 호출하기 전에 해당 퍼미션을 사용자가 허용했는지의 여부를 확인해야 하며, 아래 코드와 같다.

if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 100);
}

위의 코드 예는 READ_EXTERNAL_STORAGE라는 외장 메모리의 파일을 읽기 위한 퍼미션을 사용자가 허용했는지를 확인하고, 아직 사용자가 허용하지 않았다면 퍼미션 허용 대화상자를 아래처럼 표시한다는 것이다.

퍼미션 허용 대화상자가 표시되고 사용자가 해당 퍼미션에 대한 허용 여부는 아래와 같은 콜백함수에 의해 확인이 가능하다.

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if(requestCode == 100 && grantResults.length > 0) {
        if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 사용자가 퍼미션을 허용했으므로, 해당 퍼미션이 필요한 API 호출이 가능한 시점
            // ..
        }
    }
}

Javascript의 배열(Array)의 정렬(Sort)

사람이 처리할 수 있는 데이터의 양에는 한계가 있고, 컴퓨터는 이러한 한계를 가진 인간을 보조해주고 인간이 쉽고 빠르게 정보를 파악해 주는 기능을 제공합니다. 검색 결과 등과 같은 정보를 정렬하지 않고 사용자에게 제공할 경우 사용자는 자신이 찾고자 하는 데이터를 찾아 헤매게 됩니다. 하지만 정렬된 데이터라면 빠르게 데이터를 찾을 수 있습니다.

검색 결과를 담기에 적당한 자료 구조는 배열입니다. 특히 Javascript에서는요. 이러한 배열을 정렬하는 코드를 정리해 둡니다.

var numbers = [4, 2, 5, 1, 3];
numbers.sort(function(a, b) {
    return a - b;
});

위의 코드는 배열에 담긴 요소들을 정렬한 결과로 원래의 배열을 변경합니다.

아래는 배열에 담긴 데이터가 Key-Value를 가지는 Object 항목으로 구성된 경우에 대한 정렬 예입니다.

var items = [
    { name: 'Edward', value: 21 },
    { name: 'Sharpe', value: 37 },
    { name: 'And', value: 45 },
    { name: 'The', value: -12 },
    { name: 'Magnetic' },
    { name: 'Zeros', value: 37 }
];

items.sort(function(a, b) {
    var nameA = a.name;
    var nameB = b.name;

    if (nameA < nameB) {
        return -1;
    }

    if (nameA > nameB) {
        return 1;
    }

    return 0;
});

정렬 못지 않게 중요한 것이 필터링(Filtering)입니다. 검색 결과 중 내가 보고자 하는 데이터만을 보고, 아닌 것은 보여주지 않는 것이죠. 정렬과 필터링 기능은 기본적인 기능이지만, 제법 많은 프로그램에서 놓치고 있는 기능이기도 합니다.