├── Makefile ├── .github └── workflows │ └── ci.yml ├── LICENSE.md ├── README.md ├── natural_sort_test.cpp └── natural_sort.hpp /Makefile: -------------------------------------------------------------------------------- 1 | CC = g++ 2 | CFLAGS = -Wall 3 | 4 | test: natural_sort_test.cpp natural_sort.hpp 5 | $(CC) $(CFLAGS) -o $@ $< 6 | ./$@ 7 | 8 | clean: 9 | rm -f test 10 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v2 11 | 12 | - name: Test 13 | shell: bash 14 | run: make test 15 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Gagan Kumar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NaturalSort ![CI](https://github.com/scopeInfinity/NaturalSort/workflows/CI/badge.svg) 2 | C++ Header File for Natural Comparison and Natural Sort 3 | 4 | 5 | ##### Calling Methods 6 | 7 | * __For Natural Sorting__ 8 | 9 | void SI::natural::sort(Container); 10 | 11 | void SI::natural::sort(IteratorBegin,IteratorEnd); 12 | 13 | void SI::natural::sort(CArray); 14 | 15 | 16 | * __For Natural Comparision__ 17 | 18 | bool SI::natural::compare(String lhs,String rhs); 19 | bool SI::natural::compare(char *const lhs,char *const rhs); 20 | 21 | Here we can have 22 | 23 | std::vector as Container 24 | String as std::string 25 | CArray as std::string[CArraySize] 26 | 27 | 28 | 29 | 30 | 31 | ##### Example 32 | 33 | * __Inputs__ 34 | 35 | Hello 100 36 | Hello 34 37 | Hello 9 38 | Hello 25 39 | Hello 10 40 | Hello 8 41 | 42 | * __Normal Sort Output__ 43 | 44 | Hello 10 45 | Hello 100 46 | Hello 25 47 | Hello 34 48 | Hello 8 49 | Hello 9 50 | 51 | * __Natural Sort Output__ 52 | 53 | Hello 8 54 | Hello 9 55 | Hello 10 56 | Hello 25 57 | Hello 34 58 | Hello 100 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /natural_sort_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "natural_sort.hpp" 6 | 7 | #define ADD_TEST(test_method) { \ 8 | std::cout<<"Running test " #test_method "..."<("Hello 32", "Hello 023")); 19 | ASSERT_FALSE(SI::natural::compare("Hello 32a", "Hello 32")); 20 | ASSERT_TRUE(SI::natural::compare("Hello 32", "Hello 32a")); 21 | ASSERT_FALSE(SI::natural::compare("Hello 32.1", "Hello 32")); 22 | ASSERT_TRUE(SI::natural::compare("Hello 32", "Hello 32.1")); 23 | ASSERT_FALSE(SI::natural::compare("Hello 32", "Hello 32")); 24 | } 25 | 26 | void test_sort_vector() { 27 | std::vector data = { 28 | "Hello 100", 29 | "Hello 34", 30 | "Hello 9", 31 | "Hello 25", 32 | "Hello 10", 33 | "Hello 8", 34 | }; 35 | std::vector want = { 36 | "Hello 8", 37 | "Hello 9", 38 | "Hello 10", 39 | "Hello 25", 40 | "Hello 34", 41 | "Hello 100", 42 | }; 43 | SI::natural::sort(data); 44 | ASSERT_EQ(data, want); 45 | } 46 | 47 | void test_sort_array() { 48 | const int SZ = 3; 49 | std::string data[SZ] = { 50 | "Hello 100", 51 | "Hello 25", 52 | "Hello 34", 53 | }; 54 | std::string want[SZ] = { 55 | "Hello 25", 56 | "Hello 34", 57 | "Hello 100", 58 | }; 59 | 60 | SI::natural::sort(data); 61 | for(int i=0; i data = { 68 | "Hello 1", 69 | "Hello 10", 70 | "Hello 10.3", 71 | "Hello 2", 72 | "Hello 10.23", 73 | "Hello 10.230", 74 | }; 75 | std::vector want = { 76 | "Hello 1", 77 | "Hello 2", 78 | "Hello 10", 79 | "Hello 10.23", 80 | "Hello 10.230", 81 | "Hello 10.3", 82 | }; 83 | 84 | SI::natural::sort(data); 85 | ASSERT_EQ(data, want); 86 | } 87 | 88 | int main() 89 | { 90 | ADD_TEST(test_compare); 91 | ADD_TEST(test_sort_vector); 92 | ADD_TEST(test_sort_array); 93 | ADD_TEST(test_sort_float); 94 | return 0; 95 | } -------------------------------------------------------------------------------- /natural_sort.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | Copyright (c) 2016 Gagan Kumar(scopeInfinity) 4 | Complete License at https://raw.githubusercontent.com/scopeInfinity/NaturalSort/master/LICENSE.md 5 | */ 6 | 7 | /********************************************************************** 8 | Calling Methods : 9 | 10 | //For Natural Sorting 11 | void SI::natural::sort(Container); 12 | void SI::natural::sort(IteratorBegin,IteratorEnd); 13 | void SI::natural::sort(CArray); 14 | 15 | //For Natural Comparision 16 | bool SI::natural::compare(String lhs,String rhs); 17 | bool SI::natural::compare(char *const lhs,char *const rhs); 18 | 19 | Here we can have 20 | std::vector as Container 21 | String as std::string 22 | CArray as std::string[CArraySize] 23 | 24 | ***********************************************************************/ 25 | #ifndef SI_sort_HPP 26 | #define SI_sort_HPP 27 | #include 28 | #include 29 | #include 30 | 31 | 32 | namespace SI 33 | { 34 | namespace natural 35 | { 36 | namespace detail 37 | { 38 | /********** Compare Two Character CaseInsensitive ********/ 39 | template 40 | bool natural_less(const ElementType &lhs,const ElementType &rhs) 41 | { 42 | if(tolower(lhs) 48 | bool is_not_digit(const ElementType &x) 49 | { 50 | return !isdigit(x); 51 | } 52 | 53 | /********** Compare Two Iterators CaseInsensitive ********/ 54 | template 55 | struct comp_over_iterator 56 | { 57 | int operator()(const Iterator &lhs,const Iterator &rhs) const 58 | { 59 | if(natural_less(*lhs,*rhs)) 60 | return -1; 61 | if(natural_less(*rhs,*lhs)) 62 | return +1; 63 | return 0; 64 | } 65 | }; 66 | 67 | /**************************************************** 68 | Comparing two SubString from (Begin,End Iterator each) 69 | with only digits. 70 | Usage : 71 | int compare_number()(\ 72 | FirstNumberBeginIterator,FirstNumberEndIterator,isFirstNumberFractionalPart\ 73 | SecondNumberBeginIterator,SecondNumberEndIterator,isSecondNumberFractionalPart\ 74 | ); 75 | 76 | Returns : 77 | -1 - Number1 < Number2 78 | 0 - Number1 == Number2 79 | 1 - Number1 > Number2 80 | 81 | ***************************************************/ 82 | 83 | 84 | template 85 | struct compare_number 86 | { 87 | private: 88 | //If Number is Itself fractional Part 89 | int fractional(Iterator lhsBegin,Iterator lhsEnd, Iterator rhsBegin,Iterator rhsEnd) 90 | { 91 | while(lhsBegin()(lhsBegin,rhsBegin); 94 | if(local_compare!=0) 95 | return local_compare; 96 | lhsBegin++; 97 | rhsBegin++; 98 | } 99 | while(lhsBeginrhsEnd-rhsBegin) 118 | return +1; 119 | 120 | //Equal In length 121 | while(lhsBegin()(lhsBegin,rhsBegin); 124 | if(local_compare!=0) 125 | return local_compare; 126 | lhsBegin++; 127 | rhsBegin++; 128 | } 129 | return 0; 130 | } 131 | 132 | 133 | public: 134 | int operator()(\ 135 | Iterator lhsBegin,Iterator lhsEnd,bool isFractionalPart1,\ 136 | Iterator rhsBegin,Iterator rhsEnd,bool isFractionalPart2) 137 | { 138 | if(isFractionalPart1 && !isFractionalPart2) 139 | return true; //0=1 140 | if(!isFractionalPart1 && isFractionalPart2) 141 | return false; //0=1 142 | 143 | //isFractionPart1 == isFactionalPart2 144 | if(isFractionalPart1) 145 | return fractional(lhsBegin,lhsEnd,rhsBegin,rhsEnd); 146 | else 147 | return non_fractional(lhsBegin,lhsEnd,rhsBegin,rhsEnd); 148 | } 149 | }; 150 | 151 | 152 | 153 | 154 | 155 | }// namespace detail 156 | 157 | /*********************************************************************** 158 | Natural Comparision of Two String using both's (Begin and End Iterator) 159 | 160 | Returns : 161 | -1 - String1 < String2 162 | 0 - String1 == String2 163 | 1 - String1 > String2 164 | 165 | Suffix 1 represents for components of 1st String 166 | Suffix 2 represents for components of 2nd String 167 | ************************************************************************/ 168 | 169 | template 170 | bool _compare(\ 171 | const Iterator &lhsBegin,const Iterator &lhsEnd,\ 172 | const Iterator &rhsBegin,const Iterator &rhsEnd) 173 | { 174 | Iterator current1 = lhsBegin,current2 = rhsBegin; 175 | 176 | //Flag for Space Found Check 177 | bool flag_found_space1 = false,flag_found_space2 = false; 178 | 179 | 180 | while(current1!=lhsEnd && current2!=rhsEnd) 181 | { 182 | //Ignore More than One Continous Space 183 | 184 | /****************************************** 185 | For HandlingComparision Like 186 | Hello 9 187 | Hello 10 188 | Hello 123 189 | ******************************************/ 190 | while(flag_found_space1 && current1!=lhsEnd && *current1==' ') current1++; 191 | flag_found_space1=false; 192 | if(*current1==' ') flag_found_space1 = true; 193 | 194 | while(flag_found_space2 && current2!=rhsEnd && *current2==' ') current2++; 195 | flag_found_space2=false; 196 | if(*current2==' ') flag_found_space2 = true; 197 | 198 | 199 | if( !isdigit(*current1 ) || !isdigit(*current2)) 200 | { 201 | // Normal comparision if any of character is non digit character 202 | if(detail::natural_less(*current1,*current2)) 203 | return true; 204 | if(detail::natural_less(*current2,*current1)) 205 | return false; 206 | current1++; 207 | current2++; 208 | } 209 | else 210 | { 211 | /********************************* 212 | Capture Numeric Part of Both String 213 | and then using it to compare Both 214 | 215 | ***********************************/ 216 | Iterator last_nondigit1 = std::find_if(current1,lhsEnd,detail::is_not_digit); 217 | Iterator last_nondigit2 = std::find_if(current2,rhsEnd,detail::is_not_digit); 218 | 219 | int result = detail::compare_number()(\ 220 | current1,last_nondigit1,(current1>lhsBegin && *(current1-1)=='.'), \ 221 | current2,last_nondigit2,(current2>rhsBegin && *(current2-1)=='.')); 222 | if(result<0) 223 | return true; 224 | if(result>0) 225 | return false; 226 | current1 = last_nondigit1; 227 | current2 = last_nondigit2; 228 | } 229 | } 230 | 231 | if (current1 == lhsEnd && current2 == rhsEnd) { 232 | return false; 233 | } else { 234 | return current1 == lhsEnd; 235 | } 236 | } 237 | 238 | template 239 | inline bool compare(const String &first ,const String &second) 240 | { 241 | return _compare(first.begin(),first.end(),second.begin(),second.end()); 242 | } 243 | template<> 244 | inline bool compare(char *const &first ,char *const &second) 245 | { 246 | char* it1 = first; 247 | while(*it1!='\0')it1++; 248 | char* it2 = second; 249 | while(*it2!='\0')it2++; 250 | return _compare(first,it1,second,it2); 251 | } 252 | 253 | 254 | template 255 | inline void sort(Container &container) 256 | { 257 | std::sort(container.begin(),container.end(),compare); 258 | } 259 | 260 | template 261 | inline void sort(const Iterator &first,const Iterator &end) 262 | { 263 | std::sort(first,end,compare); 264 | } 265 | 266 | template 267 | inline void sort(ValueType* const first,ValueType* const end) 268 | { 269 | std::sort(first,end,compare); 270 | } 271 | 272 | template 273 | inline void sort(ValueType container[N]) 274 | { 275 | std::sort(&container[0],&container[0]+N,compare); 276 | } 277 | 278 | }//namespace natural 279 | }//namespace SI 280 | 281 | #endif 282 | --------------------------------------------------------------------------------