PHP 배열 정렬 문제 해결하기

프리터코더·2025년 5월 27일
0

php 문제 해결

목록 보기
37/79

1. 기본 정렬 함수 선택 및 사용법

문제: 상황에 맞지 않는 정렬 함수 사용으로 인한 오류

<?php
// 다양한 정렬 함수 비교

// 1. 값 기준 정렬 (키 유지 안함)
$numbers = [3, 1, 4, 1, 5, 9, 2, 6];
$sorted_numbers = $numbers;

echo "원본 배열:\n";
print_r($numbers);

// sort() - 오름차순, 키 재배열
sort($sorted_numbers);
echo "sort() 결과:\n";
print_r($sorted_numbers);

// rsort() - 내림차순, 키 재배열
$rsorted_numbers = $numbers;
rsort($rsorted_numbers);
echo "rsort() 결과:\n";
print_r($rsorted_numbers);

// 2. 값 기준 정렬 (키 유지)
$fruits = [
    'b' => 'banana',
    'a' => 'apple',
    'c' => 'cherry',
    'd' => 'date'
];

echo "\n연관 배열 원본:\n";
print_r($fruits);

// asort() - 값 기준 오름차순, 키 유지
$asorted_fruits = $fruits;
asort($asorted_fruits);
echo "asort() 결과 (값 기준, 키 유지):\n";
print_r($asorted_fruits);

// arsort() - 값 기준 내림차순, 키 유지
$arsorted_fruits = $fruits;
arsort($arsorted_fruits);
echo "arsort() 결과 (값 기준 내림차순, 키 유지):\n";
print_r($arsorted_fruits);

// 3. 키 기준 정렬
$ksorted_fruits = $fruits;
ksort($ksorted_fruits);
echo "ksort() 결과 (키 기준 오름차순):\n";
print_r($ksorted_fruits);

$krsorted_fruits = $fruits;
krsort($krsorted_fruits);
echo "krsort() 결과 (키 기준 내림차순):\n";
print_r($krsorted_fruits);
?>

2. 사용자 정의 정렬 함수

문제: 복잡한 정렬 조건을 기본 함수로 처리할 수 없음

<?php
// 사용자 정의 정렬 예제

$products = [
    ['name' => 'iPhone', 'price' => 1200000, 'rating' => 4.5, 'category' => 'phone'],
    ['name' => 'Galaxy', 'price' => 1000000, 'rating' => 4.3, 'category' => 'phone'],
    ['name' => 'iPad', 'price' => 800000, 'rating' => 4.7, 'category' => 'tablet'],
    ['name' => 'MacBook', 'price' => 2000000, 'rating' => 4.8, 'category' => 'laptop'],
    ['name' => 'Surface', 'price' => 1500000, 'rating' => 4.2, 'category' => 'laptop']
];

echo "원본 상품 목록:\n";
foreach ($products as $product) {
    echo "{$product['name']} - {$product['price']}원 - 평점: {$product['rating']}\n";
}

// 1. 가격 기준 오름차순 정렬
$price_sorted = $products;
usort($price_sorted, function($a, $b) {
    return $a['price'] <=> $b['price'];
});

echo "\n가격 기준 오름차순:\n";
foreach ($price_sorted as $product) {
    echo "{$product['name']} - {$product['price']}원\n";
}

// 2. 평점 기준 내림차순 정렬
$rating_sorted = $products;
usort($rating_sorted, function($a, $b) {
    return $b['rating'] <=> $a['rating'];
});

echo "\n평점 기준 내림차순:\n";
foreach ($rating_sorted as $product) {
    echo "{$product['name']} - 평점: {$product['rating']}\n";
}

// 3. 다중 조건 정렬 (카테고리 -> 가격)
$multi_sorted = $products;
usort($multi_sorted, function($a, $b) {
    // 먼저 카테고리로 정렬
    $category_compare = strcmp($a['category'], $b['category']);
    if ($category_compare !== 0) {
        return $category_compare;
    }
    
    // 카테고리가 같으면 가격으로 정렬
    return $a['price'] <=> $b['price'];
});

echo "\n카테고리 -> 가격 순 정렬:\n";
foreach ($multi_sorted as $product) {
    echo "{$product['category']} - {$product['name']} - {$product['price']}원\n";
}
?>

3. 다차원 배열 정렬

문제: 복잡한 다차원 배열에서 특정 필드로 정렬하기 어려움

<?php
// 다차원 배열 정렬 클래스
class ArraySorter {
    
    /**
     * 다차원 배열을 특정 키로 정렬
     */
    public static function sortByKey($array, $key, $direction = 'asc') {
        usort($array, function($a, $b) use ($key, $direction) {
            $valueA = self::getNestedValue($a, $key);
            $valueB = self::getNestedValue($b, $key);
            
            $result = $valueA <=> $valueB;
            
            return $direction === 'desc' ? -$result : $result;
        });
        
        return $array;
    }
    
    /**
     * 중첩된 키로 값 가져오기 (예: 'user.profile.age')
     */
    private static function getNestedValue($array, $key) {
        $keys = explode('.', $key);
        $value = $array;
        
        foreach ($keys as $k) {
            if (is_array($value) && isset($value[$k])) {
                $value = $value[$k];
            } else {
                return null;
            }
        }
        
        return $value;
    }
    
    /**
     * 다중 조건 정렬
     */
    public static function sortByMultiple($array, $criteria) {
        usort($array, function($a, $b) use ($criteria) {
            foreach ($criteria as $criterion) {
                $key = $criterion['key'];
                $direction = $criterion['direction'] ?? 'asc';
                
                $valueA = self::getNestedValue($a, $key);
                $valueB = self::getNestedValue($b, $key);
                
                $result = $valueA <=> $valueB;
                
                if ($result !== 0) {
                    return $direction === 'desc' ? -$result : $result;
                }
            }
            
            return 0;
        });
        
        return $array;
    }
}

// 테스트 데이터
$users = [
    [
        'id' => 1,
        'name' => '김철수',
        'profile' => [
            'age' => 30,
            'city' => '서울',
            'score' => 85
        ],
        'created_at' => '2023-01-15'
    ],
    [
        'id' => 2,
        'name' => '이영희',
        'profile' => [
            'age' => 25,
            'city' => '부산',
            'score' => 92
        ],
        'created_at' => '2023-02-20'
    ],
    [
        'id' => 3,
        'name' => '박민수',
        'profile' => [
            'age' => 35,
            'city' => '서울',
            'score' => 78
        ],
        'created_at' => '2023-01-10'
    ]
];

echo "원본 데이터:\n";
foreach ($users as $user) {
    echo "{$user['name']} - 나이: {$user['profile']['age']} - 점수: {$user['profile']['score']}\n";
}

// 나이 기준 정렬
$age_sorted = ArraySorter::sortByKey($users, 'profile.age');
echo "\n나이 기준 오름차순:\n";
foreach ($age_sorted as $user) {
    echo "{$user['name']} - 나이: {$user['profile']['age']}\n";
}

// 점수 기준 내림차순 정렬
$score_sorted = ArraySorter::sortByKey($users, 'profile.score', 'desc');
echo "\n점수 기준 내림차순:\n";
foreach ($score_sorted as $user) {
    echo "{$user['name']} - 점수: {$user['profile']['score']}\n";
}

// 다중 조건 정렬 (도시 -> 나이)
$multi_sorted = ArraySorter::sortByMultiple($users, [
    ['key' => 'profile.city', 'direction' => 'asc'],
    ['key' => 'profile.age', 'direction' => 'asc']
]);

echo "\n도시 -> 나이 순 정렬:\n";
foreach ($multi_sorted as $user) {
    echo "{$user['profile']['city']} - {$user['name']} - 나이: {$user['profile']['age']}\n";
}
?>

4. 자연 정렬 및 특수 정렬

문제: 숫자가 포함된 문자열이나 특수한 형태의 데이터 정렬

<?php
// 자연 정렬 및 특수 정렬 예제

// 1. 자연 정렬 (숫자 포함 문자열)
$files = [
    'file1.txt',
    'file10.txt',
    'file2.txt',
    'file20.txt',
    'file3.txt'
];

echo "일반 정렬:\n";
$normal_sorted = $files;
sort($normal_sorted);
print_r($normal_sorted);

echo "자연 정렬:\n";
$natural_sorted = $files;
natsort($natural_sorted);
print_r($natural_sorted);

// 2. 버전 번호 정렬
$versions = ['1.10.0', '1.2.0', '1.9.0', '2.0.0', '1.2.1'];

echo "\n버전 번호 자연 정렬:\n";
$version_sorted = $versions;
usort($version_sorted, 'version_compare');
print_r($version_sorted);

// 3. 날짜 정렬
$dates = [
    '2023-12-01',
    '2023-01-15',
    '2023-06-30',
    '2023-03-20'
];

echo "\n날짜 정렬:\n";
$date_sorted = $dates;
usort($date_sorted, function($a, $b) {
    return strtotime($a) <=> strtotime($b);
});
print_r($date_sorted);

// 4. 한글 정렬 (로케일 기반)
$korean_names = ['김철수', '이영희', '박민수', '최지영', '정다은'];

echo "\n한글 이름 정렬:\n";
$korean_sorted = $korean_names;

// 로케일 설정 (시스템에 따라 다를 수 있음)
if (setlocale(LC_COLLATE, 'ko_KR.UTF-8') !== false) {
    usort($korean_sorted, function($a, $b) {
        return strcoll($a, $b);
    });
} else {
    // 로케일 설정 실패 시 기본 정렬
    sort($korean_sorted);
}
print_r($korean_sorted);

// 5. 사용자 정의 우선순위 정렬
$priorities = ['high', 'medium', 'low', 'urgent', 'normal'];
$priority_order = ['urgent' => 1, 'high' => 2, 'medium' => 3, 'normal' => 4, 'low' => 5];

echo "\n우선순위 정렬:\n";
$priority_sorted = $priorities;
usort($priority_sorted, function($a, $b) use ($priority_order) {
    return $priority_order[$a] <=> $priority_order[$b];
});
print_r($priority_sorted);
?>

5. 성능 최적화 및 대용량 데이터 정렬

문제: 대용량 배열 정렬 시 성능 문제

<?php
// 성능 최적화된 정렬 클래스
class PerformanceSorter {
    
    /**
     * 대용량 배열을 위한 메모리 효율적 정렬
     */
    public static function efficientSort($array, $callback = null) {
        $start_memory = memory_get_usage();
        $start_time = microtime(true);
        
        if ($callback === null) {
            sort($array);
        } else {
            usort($array, $callback);
        }
        
        $end_memory = memory_get_usage();
        $end_time = microtime(true);
        
        echo "정렬 완료:\n";
        echo "- 실행 시간: " . round(($end_time - $start_time) * 1000, 2) . "ms\n";
        echo "- 메모리 사용량: " . round(($end_memory - $start_memory) / 1024, 2) . "KB\n";
        
        return $array;
    }
    
    /**
     * 청크 단위로 나누어 정렬 (메모리 절약)
     */
    public static function chunkSort($array, $chunk_size = 1000, $callback = null) {
        $chunks = array_chunk($array, $chunk_size);
        $sorted_chunks = [];
        
        foreach ($chunks as $chunk) {
            if ($callback === null) {
                sort($chunk);
            } else {
                usort($chunk, $callback);
            }
            $sorted_chunks[] = $chunk;
        }
        
        // 정렬된 청크들을 병합
        return self::mergeChunks($sorted_chunks, $callback);
    }
    
    /**
     * 정렬된 청크들을 병합
     */
    private static function mergeChunks($chunks, $callback = null) {
        $result = [];
        
        foreach ($chunks as $chunk) {
            $result = self::mergeTwoArrays($result, $chunk, $callback);
        }
        
        return $result;
    }
    
    /**
     * 두 정렬된 배열을 병합
     */
    private static function mergeTwoArrays($arr1, $arr2, $callback = null) {
        $result = [];
        $i = $j = 0;
        
        while ($i < count($arr1) && $j < count($arr2)) {
            $compare = 0;
            
            if ($callback === null) {
                $compare = $arr1[$i] <=> $arr2[$j];
            } else {
                $compare = $callback($arr1[$i], $arr2[$j]);
            }
            
            if ($compare <= 0) {
                $result[] = $arr1[$i++];
            } else {
                $result[] = $arr2[$j++];
            }
        }
        
        // 남은 요소들 추가
        while ($i < count($arr1)) {
            $result[] = $arr1[$i++];
        }
        
        while ($j < count($arr2)) {
            $result[] = $arr2[$j++];
        }
        
        return $result;
    }
    
    /**
     * 인덱스 기반 정렬 (원본 배열 수정 없이)
     */
    public static function indexSort($array, $callback = null) {
        $indices = array_keys($array);
        
        if ($callback === null) {
            usort($indices, function($a, $b) use ($array) {
                return $array[$a] <=> $array[$b];
            });
        } else {
            usort($indices, function($a, $b) use ($array, $callback) {
                return $callback($array[$a], $array[$b]);
            });
        }
        
        return $indices;
    }
}

// 대용량 데이터 테스트
echo "대용량 배열 정렬 테스트:\n";

// 10만개 랜덤 숫자 생성
$large_array = [];
for ($i = 0; $i < 100000; $i++) {
    $large_array[] = rand(1, 1000000);
}

echo "배열 크기: " . count($large_array) . " 개\n";

// 일반 정렬
$test_array = $large_array;
echo "\n일반 정렬:\n";
PerformanceSorter::efficientSort($test_array);

// 청크 정렬
$test_array = $large_array;
echo "\n청크 정렬 (청크 크기: 5000):\n";
$chunk_sorted = PerformanceSorter::chunkSort($test_array, 5000);

// 인덱스 정렬
echo "\n인덱스 정렬:\n";
$small_test = array_slice($large_array, 0, 1000);
$indices = PerformanceSorter::indexSort($small_test);
echo "정렬된 인덱스 처음 10개: " . implode(', ', array_slice($indices, 0, 10)) . "\n";
?>

6. 정렬 오류 디버깅 및 검증

문제: 정렬 결과가 예상과 다르거나 오류 발생

<?php
// 정렬 디버깅 및 검증 클래스
class SortingDebugger {
    
    /**
     * 정렬 함수의 안정성 검증
     */
    public static function validateSortFunction($callback, $test_cases = null) {
        if ($test_cases === null) {
            $test_cases = [
                [1, 2],
                [2, 1],
                [1, 1],
                ['a', 'b'],
                ['b', 'a'],
                [null, 1],
                [1, null]
            ];
        }
        
        echo "정렬 함수 검증:\n";
        
        foreach ($test_cases as $case) {
            $result = $callback($case[0], $case[1]);
            $status = self::validateComparisonResult($result);
            
            echo "- {$case[0]} vs {$case[1]} = $result ($status)\n";
        }
    }
    
    /**
     * 비교 결과 검증
     */
    private static function validateComparisonResult($result) {
        if ($result < 0) return "첫 번째가 작음";
        if ($result > 0) return "첫 번째가 큼";
        return "같음";
    }
    
    /**
     * 정렬 결과 검증
     */
    public static function verifySortResult($array, $callback = null) {
        $is_sorted = true;
        $errors = [];
        
        for ($i = 0; $i < count($array) - 1; $i++) {
            $compare = 0;
            
            if ($callback === null) {
                $compare = $array[$i] <=> $array[$i + 1];
            } else {
                $compare = $callback($array[$i], $array[$i + 1]);
            }
            
            if ($compare > 0) {
                $is_sorted = false;
                $errors[] = "인덱스 $i: {$array[$i]} > {$array[$i + 1]}";
            }
        }
        
        if ($is_sorted) {
            echo "✓ 정렬이 올바르게 되었습니다.\n";
        } else {
            echo "✗ 정렬 오류 발견:\n";
            foreach ($errors as $error) {
                echo "  - $error\n";
            }
        }
        
        return $is_sorted;
    }
    
    /**
     * 정렬 성능 비교
     */
    public static function compareSortPerformance($array, $methods) {
        echo "정렬 성능 비교 (배열 크기: " . count($array) . "):\n";
        
        foreach ($methods as $name => $callback) {
            $test_array = $array; // 복사본 생성
            
            $start_time = microtime(true);
            $start_memory = memory_get_usage();
            
            $callback($test_array);
            
            $end_time = microtime(true);
            $end_memory = memory_get_usage();
            
            $time_taken = round(($end_time - $start_time) * 1000, 2);
            $memory_used = round(($end_memory - $start_memory) / 1024, 2);
            
            echo "- $name: {$time_taken}ms, {$memory_used}KB\n";
        }
    }
}

// 디버깅 예제
echo "정렬 디버깅 예제:\n\n";

// 1. 잘못된 비교 함수 예제
echo "1. 잘못된 비교 함수:\n";
$wrong_callback = function($a, $b) {
    // 잘못된 예: 일관성 없는 비교
    if ($a == $b) return 1; // 같을 때 0이 아닌 1 반환
    return $a <=> $b;
};

SortingDebugger::validateSortFunction($wrong_callback);

// 2. 올바른 비교 함수
echo "\n2. 올바른 비교 함수:\n";
$correct_callback = function($a, $b) {
    return $a <=> $b;
};

SortingDebugger::validateSortFunction($correct_callback);

// 3. 정렬 결과 검증
echo "\n3. 정렬 결과 검증:\n";
$test_array = [3, 1, 4, 1, 5, 9, 2, 6];
sort($test_array);
SortingDebugger::verifySortResult($test_array);

// 잘못 정렬된 배열 테스트
$wrong_sorted = [1, 3, 2, 4, 5];
echo "\n잘못 정렬된 배열 검증:\n";
SortingDebugger::verifySortResult($wrong_sorted);

// 4. 성능 비교
echo "\n4. 정렬 방법별 성능 비교:\n";
$performance_test_array = [];
for ($i = 0; $i < 10000; $i++) {
    $performance_test_array[] = rand(1, 1000);
}

$sort_methods = [
    'sort()' => function(&$arr) { sort($arr); },
    'usort() 기본' => function(&$arr) { 
        usort($arr, function($a, $b) { return $a <=> $b; }); 
    },
    'array_multisort()' => function(&$arr) { 
        array_multisort($arr, SORT_NUMERIC, SORT_ASC); 
    }
];

SortingDebugger::compareSortPerformance($performance_test_array, $sort_methods);
?>

주요 정렬 함수 요약

함수용도키 보존정렬 기준
sort()기본 오름차순
rsort()기본 내림차순
asort()연관배열 오름차순
arsort()연관배열 내림차순
ksort()키 기준 오름차순
krsort()키 기준 내림차순
usort()사용자 정의사용자 함수
uasort()사용자 정의 (키 보존)사용자 함수
uksort()키 기준 사용자 정의사용자 함수
natsort()자연 정렬자연순서

주의사항

  • 비교 함수: usort() 사용 시 일관성 있는 비교 함수 작성
  • 메모리 사용량: 대용량 배열 정렬 시 메모리 제한 고려
  • 원본 보존: 원본 배열을 보존해야 할 경우 복사본 사용
  • 데이터 타입: 혼합된 데이터 타입 정렬 시 예상치 못한 결과 발생 가능
  • 성능: 단순한 정렬은 기본 함수, 복잡한 조건은 사용자 정의 함수 사용
profile
일용직 개발자. freetercoder@gmail.com

0개의 댓글