Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 06fd5ea31a | |||
| 36f17aa442 | |||
| 6c891952f4 | |||
| 3bac27b7a8 | |||
| 94d4b11aec | |||
| 4be394e313 | |||
| 891ecf6e2d | |||
| 736e0ea7da | |||
| c63ab5a3aa | |||
| f06553a74d | |||
| 72f8248df1 | |||
| 8ef9e9263f | |||
| ab03176239 | |||
| 4df7502e09 | |||
| 9bf57b0504 | |||
| 92ae96cf2a | |||
| b120d60c55 | |||
| cb4cb74a00 | |||
| 6bbbee5447 | |||
| e5bc9eceae | |||
| f153d5df84 | |||
| 1164a68fc8 | |||
| 596396a8e8 | |||
| 027494878b | |||
| 74f50a4c71 | |||
| ad37e7c91f | |||
| 4c236e1b82 | |||
| d78cc12e8d | |||
| 8866b3e301 | |||
| c3c5931187 | |||
| af7d973978 | |||
| ab67e0fea8 | |||
| 14e4501035 | |||
| a569fc06f5 | |||
| 465ea731a3 | |||
| 8a7247d9d4 | |||
| 909bed0ada | |||
| 5a5a8cc62d | |||
| e44b3a7f4c | |||
| ecdc5f971b | |||
| efcb0db2e0 | |||
| 1743d4dc3f | |||
| fb67bd25b0 | |||
| 69c8843b76 | |||
| 36ff800650 | |||
| 13101672c2 | |||
| 7abeecbf58 | |||
| 13bf59d5bd | |||
| 47f66b1abd | |||
| 42bb82b87b | |||
| 30c36e66fd | |||
| 3f6f0750cf | |||
| ae2cc6dd55 | |||
| 0d48ec751a | |||
| d2dec8bba0 | |||
| e8795b26df | |||
| ab7784b97f | |||
| 9adcc6f84c | |||
| 4ccfd1b9a3 |
@@ -7,17 +7,36 @@ on:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
macos:
|
||||
name: macOS (Xcode ${{ matrix.xcode }})
|
||||
runs-on: macos-latest
|
||||
macos-11:
|
||||
name: macOS 11 (Xcode ${{ matrix.xcode }})
|
||||
runs-on: macos-11
|
||||
strategy:
|
||||
matrix:
|
||||
# See https://github.com/actions/virtual-environments/tree/master/images/macos
|
||||
# No Xcode with support for Swift 4.2 available
|
||||
# https://github.com/actions/virtual-environments/blob/main/images/macos/macos-11-Readme.md#xcode
|
||||
xcode:
|
||||
- "11.5_beta" # Swift 5.2.4
|
||||
- "11.4" # Swift 5.2
|
||||
- "11" # Swift 5.1
|
||||
- "13.0" # Swift 5.5
|
||||
- "12.5.1" # Swift 5.4.2
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Select Xcode
|
||||
run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app
|
||||
- name: Build
|
||||
run: swift build -v
|
||||
- name: Test
|
||||
run: swift test -v
|
||||
|
||||
macos-10_15:
|
||||
name: macOS 10.15 (Xcode ${{ matrix.xcode }})
|
||||
runs-on: macos-10.15
|
||||
strategy:
|
||||
matrix:
|
||||
# https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md#xcode
|
||||
xcode:
|
||||
- "12.3" # Swift 5.3.2
|
||||
- "12.2" # Swift 5.3.1
|
||||
- "11.7" # Swift 5.2.4
|
||||
- "11.3.1" # Swift 5.1.3
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
@@ -32,7 +51,7 @@ jobs:
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: swift:5.2
|
||||
image: swift:5.3
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
os: osx
|
||||
language: swift
|
||||
|
||||
xcode_project: OrderedDictionary.xcodeproj
|
||||
|
||||
matrix:
|
||||
include:
|
||||
# macOS 10.13 / Xcode 10.0 / Swift 4.2 / Target: macOS
|
||||
- osx_image: xcode10
|
||||
xcode_scheme: OrderedDictionary-Mac
|
||||
env:
|
||||
- XCODE_SDK=macosx
|
||||
- XCODE_ACTION="build test"
|
||||
- XCODE_DESTINATION="arch=x86_64"
|
||||
- XCODE_PLAYGROUND_TARGET="x86_64-apple-macosx10.10"
|
||||
# macOS 10.13 / Xcode 10.0 / Swift 4.2 / Target: iOS
|
||||
- osx_image: xcode10
|
||||
xcode_scheme: OrderedDictionary-iOS
|
||||
env:
|
||||
- XCODE_SDK=iphonesimulator
|
||||
- XCODE_ACTION="build-for-testing test-without-building"
|
||||
- XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s,OS=10.1"
|
||||
|
||||
script:
|
||||
- .travis/build.sh
|
||||
@@ -1,57 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script was taken from the ReactiveSwift repository.
|
||||
# https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/script/build
|
||||
|
||||
BUILD_DIRECTORY="build"
|
||||
CONFIGURATION="Release"
|
||||
|
||||
if [[ -z $TRAVIS_XCODE_PROJECT ]]; then
|
||||
echo "Error: \$TRAVIS_XCODE_PROJECT is not set."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z $TRAVIS_XCODE_SCHEME ]]; then
|
||||
echo "Error: \$TRAVIS_XCODE_SCHEME is not set."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z $XCODE_ACTION ]]; then
|
||||
echo "Error: \$XCODE_ACTION is not set."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z $XCODE_SDK ]]; then
|
||||
echo "Error: \$XCODE_SDK is not set."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z $XCODE_DESTINATION ]]; then
|
||||
echo "Error: \$XCODE_DESTINATION is not set."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -o pipefail
|
||||
|
||||
swift --version
|
||||
|
||||
xcodebuild $XCODE_ACTION \
|
||||
-project "$TRAVIS_XCODE_PROJECT" \
|
||||
-scheme "$TRAVIS_XCODE_SCHEME" \
|
||||
-sdk "$XCODE_SDK" \
|
||||
-destination "$XCODE_DESTINATION" \
|
||||
-derivedDataPath "${BUILD_DIRECTORY}" \
|
||||
-configuration $CONFIGURATION \
|
||||
ENABLE_TESTABILITY=YES \
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS=NO \
|
||||
RUN_CLANG_STATIC_ANALYZER=NO | xcpretty
|
||||
result=$?
|
||||
|
||||
if [ "$result" -ne 0 ]; then
|
||||
exit $result
|
||||
fi
|
||||
|
||||
if [[ $XCODE_SDK = "macosx" ]]; then
|
||||
echo "SDK is ${XCODE_SDK}, validating playground..."
|
||||
. .travis/validate-playgrounds.sh
|
||||
fi
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ -z $BUILD_DIRECTORY ]]; then
|
||||
echo "Error: \$BUILD_DIRECTORY is not set."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z $XCODE_PLAYGROUND_TARGET ]]; then
|
||||
echo "Error: \$XCODE_PLAYGROUND_TARGET is not set."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PAGES_PATH=Playgrounds/OrderedDictionary.playground/Contents.swift
|
||||
|
||||
swift -v -target ${XCODE_PLAYGROUND_TARGET} \
|
||||
-D NOT_IN_PLAYGROUND \
|
||||
-F ${BUILD_DIRECTORY}/Build/Products/${CONFIGURATION} ${PAGES_PATH} > /dev/null
|
||||
|
||||
result=$?
|
||||
|
||||
rm -Rf ${BUILD_DIRECTORY}
|
||||
|
||||
exit $result
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright © 2015-2020 Lukas Kubanek
|
||||
Copyright © 2015-2021 Lukas Kubanek
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -10,11 +10,39 @@
|
||||
8048C8AB22D8911B0086B88B /* OrderedDictionary+Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8048C8AA22D8911B0086B88B /* OrderedDictionary+Deprecated.swift */; };
|
||||
8048C8AC22D8911B0086B88B /* OrderedDictionary+Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8048C8AA22D8911B0086B88B /* OrderedDictionary+Deprecated.swift */; };
|
||||
8055B0421E201C5D009DC3EE /* OrderedDictionary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8055B0381E201C5D009DC3EE /* OrderedDictionary.framework */; };
|
||||
8055B0591E201DF3009DC3EE /* OrderedDictionaryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8055B0581E201DF3009DC3EE /* OrderedDictionaryTests.swift */; };
|
||||
80A203A11F3F483700622481 /* OrderedDictionary+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80A203A01F3F483700622481 /* OrderedDictionary+Codable.swift */; };
|
||||
80A203A21F3F4C1F00622481 /* OrderedDictionary+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80A203A01F3F483700622481 /* OrderedDictionary+Codable.swift */; };
|
||||
80B28EA01E201EC9007E3A77 /* OrderedDictionary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 80B28E971E201EC8007E3A77 /* OrderedDictionary.framework */; };
|
||||
80B28EB01E201F1C007E3A77 /* OrderedDictionaryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8055B0581E201DF3009DC3EE /* OrderedDictionaryTests.swift */; };
|
||||
80BE579A252B278D00F85D45 /* XCTUnwrapShim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BE5799252B278D00F85D45 /* XCTUnwrapShim.swift */; };
|
||||
80BE579B252B278D00F85D45 /* XCTUnwrapShim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BE5799252B278D00F85D45 /* XCTUnwrapShim.swift */; };
|
||||
80BE57A5252B3F6000F85D45 /* SubscriptAmbiguityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BE57A4252B3F6000F85D45 /* SubscriptAmbiguityTests.swift */; };
|
||||
80BE57A6252B3F6000F85D45 /* SubscriptAmbiguityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BE57A4252B3F6000F85D45 /* SubscriptAmbiguityTests.swift */; };
|
||||
80BE57D4252BAB9400F85D45 /* OrderedDictionarySlice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BE57D3252BAB9400F85D45 /* OrderedDictionarySlice.swift */; };
|
||||
80BE57D5252BAB9400F85D45 /* OrderedDictionarySlice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BE57D3252BAB9400F85D45 /* OrderedDictionarySlice.swift */; };
|
||||
80BE57DF252BADEE00F85D45 /* Dictionary+OrderedDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BE57DE252BADEE00F85D45 /* Dictionary+OrderedDictionary.swift */; };
|
||||
80BE57E0252BADEE00F85D45 /* Dictionary+OrderedDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BE57DE252BADEE00F85D45 /* Dictionary+OrderedDictionary.swift */; };
|
||||
80BFD01B252B049E002B3C05 /* InitializationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD01A252B049E002B3C05 /* InitializationTests.swift */; };
|
||||
80BFD01C252B049E002B3C05 /* InitializationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD01A252B049E002B3C05 /* InitializationTests.swift */; };
|
||||
80BFD02A252B069F002B3C05 /* SortingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD029252B069F002B3C05 /* SortingTests.swift */; };
|
||||
80BFD02B252B069F002B3C05 /* SortingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD029252B069F002B3C05 /* SortingTests.swift */; };
|
||||
80BFD031252B06EB002B3C05 /* DescriptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD030252B06EB002B3C05 /* DescriptionTests.swift */; };
|
||||
80BFD032252B06EB002B3C05 /* DescriptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD030252B06EB002B3C05 /* DescriptionTests.swift */; };
|
||||
80BFD040252B0740002B3C05 /* CodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD03F252B0740002B3C05 /* CodingTests.swift */; };
|
||||
80BFD041252B0740002B3C05 /* CodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD03F252B0740002B3C05 /* CodingTests.swift */; };
|
||||
80BFD04B252B0951002B3C05 /* CapacityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD04A252B0951002B3C05 /* CapacityTests.swift */; };
|
||||
80BFD04C252B0951002B3C05 /* CapacityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD04A252B0951002B3C05 /* CapacityTests.swift */; };
|
||||
80BFD052252B09DC002B3C05 /* ReorderingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD051252B09DC002B3C05 /* ReorderingTests.swift */; };
|
||||
80BFD053252B09DC002B3C05 /* ReorderingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD051252B09DC002B3C05 /* ReorderingTests.swift */; };
|
||||
80BFD061252B0A7F002B3C05 /* RemovalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD060252B0A7F002B3C05 /* RemovalTests.swift */; };
|
||||
80BFD062252B0A7F002B3C05 /* RemovalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD060252B0A7F002B3C05 /* RemovalTests.swift */; };
|
||||
80BFD070252B0C16002B3C05 /* AccessTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD06F252B0C16002B3C05 /* AccessTests.swift */; };
|
||||
80BFD071252B0C16002B3C05 /* AccessTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD06F252B0C16002B3C05 /* AccessTests.swift */; };
|
||||
80BFD077252B0D94002B3C05 /* MapFilterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD076252B0D94002B3C05 /* MapFilterTests.swift */; };
|
||||
80BFD078252B0D94002B3C05 /* MapFilterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD076252B0D94002B3C05 /* MapFilterTests.swift */; };
|
||||
80BFD07E252B0E19002B3C05 /* UpdatesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD07D252B0E19002B3C05 /* UpdatesTests.swift */; };
|
||||
80BFD07F252B0E19002B3C05 /* UpdatesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD07D252B0E19002B3C05 /* UpdatesTests.swift */; };
|
||||
80BFD0A8252B1C96002B3C05 /* InsertionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD0A7252B1C96002B3C05 /* InsertionsTests.swift */; };
|
||||
80BFD0A9252B1C96002B3C05 /* InsertionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BFD0A7252B1C96002B3C05 /* InsertionsTests.swift */; };
|
||||
80E8E21D1E20301E00395E49 /* OrderedDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80E8E21C1E20301E00395E49 /* OrderedDictionary.swift */; };
|
||||
80E8E21F1E20425B00395E49 /* OrderedDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80E8E21C1E20301E00395E49 /* OrderedDictionary.swift */; };
|
||||
80E8E2301E2133D100395E49 /* OrderedDictionary+Description.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80E8E22F1E2133D100395E49 /* OrderedDictionary+Description.swift */; };
|
||||
@@ -39,20 +67,29 @@
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
8024947E2277123600AB44C7 /* Package@swift-4.2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Package@swift-4.2.swift"; sourceTree = "<group>"; };
|
||||
804879371E217C7700AD31A3 /* build.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = build.sh; sourceTree = "<group>"; };
|
||||
804879381E217CA100AD31A3 /* validate-playgrounds.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "validate-playgrounds.sh"; sourceTree = "<group>"; };
|
||||
8048C8AA22D8911B0086B88B /* OrderedDictionary+Deprecated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Deprecated.swift"; sourceTree = "<group>"; };
|
||||
8055B0381E201C5D009DC3EE /* OrderedDictionary.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OrderedDictionary.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8055B0411E201C5D009DC3EE /* OrderedDictionary_Mac_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OrderedDictionary_Mac_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8055B0581E201DF3009DC3EE /* OrderedDictionaryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrderedDictionaryTests.swift; sourceTree = "<group>"; };
|
||||
807AA68F1F1E587A00576474 /* OrderedDictionary+Codable.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = "OrderedDictionary+Codable.playground"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||
80A203A01F3F483700622481 /* OrderedDictionary+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Codable.swift"; sourceTree = "<group>"; };
|
||||
80B28E971E201EC8007E3A77 /* OrderedDictionary.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OrderedDictionary.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
80B28E9F1E201EC9007E3A77 /* OrderedDictionary_iOS_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OrderedDictionary_iOS_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
80B28EB41E201F81007E3A77 /* Info-Tests.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-Tests.plist"; sourceTree = "<group>"; };
|
||||
80B28EB51E201F81007E3A77 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
80B28EB71E2020DD007E3A77 /* OrderedDictionary.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = OrderedDictionary.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||
80BE5799252B278D00F85D45 /* XCTUnwrapShim.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTUnwrapShim.swift; sourceTree = "<group>"; };
|
||||
80BE57A4252B3F6000F85D45 /* SubscriptAmbiguityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptAmbiguityTests.swift; sourceTree = "<group>"; };
|
||||
80BE57D3252BAB9400F85D45 /* OrderedDictionarySlice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderedDictionarySlice.swift; sourceTree = "<group>"; };
|
||||
80BE57DE252BADEE00F85D45 /* Dictionary+OrderedDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+OrderedDictionary.swift"; sourceTree = "<group>"; };
|
||||
80BFD01A252B049E002B3C05 /* InitializationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitializationTests.swift; sourceTree = "<group>"; };
|
||||
80BFD029252B069F002B3C05 /* SortingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SortingTests.swift; sourceTree = "<group>"; };
|
||||
80BFD030252B06EB002B3C05 /* DescriptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DescriptionTests.swift; sourceTree = "<group>"; };
|
||||
80BFD03F252B0740002B3C05 /* CodingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodingTests.swift; sourceTree = "<group>"; };
|
||||
80BFD04A252B0951002B3C05 /* CapacityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapacityTests.swift; sourceTree = "<group>"; };
|
||||
80BFD051252B09DC002B3C05 /* ReorderingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReorderingTests.swift; sourceTree = "<group>"; };
|
||||
80BFD060252B0A7F002B3C05 /* RemovalTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemovalTests.swift; sourceTree = "<group>"; };
|
||||
80BFD06F252B0C16002B3C05 /* AccessTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessTests.swift; sourceTree = "<group>"; };
|
||||
80BFD076252B0D94002B3C05 /* MapFilterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapFilterTests.swift; sourceTree = "<group>"; };
|
||||
80BFD07D252B0E19002B3C05 /* UpdatesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdatesTests.swift; sourceTree = "<group>"; };
|
||||
80BFD0A7252B1C96002B3C05 /* InsertionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertionsTests.swift; sourceTree = "<group>"; };
|
||||
80DE329220F4CAFA0053EDA7 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
|
||||
80DE329320F4DD910053EDA7 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||
80DE329420F4DD910053EDA7 /* LICENSE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = "<group>"; };
|
||||
@@ -94,27 +131,15 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
804879361E217C7700AD31A3 /* Scripts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
804879371E217C7700AD31A3 /* build.sh */,
|
||||
804879381E217CA100AD31A3 /* validate-playgrounds.sh */,
|
||||
);
|
||||
path = Scripts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8055B02E1E201C5D009DC3EE = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
80DE329320F4DD910053EDA7 /* README.md */,
|
||||
80DE329420F4DD910053EDA7 /* LICENSE.md */,
|
||||
80DE329220F4CAFA0053EDA7 /* Package.swift */,
|
||||
8024947E2277123600AB44C7 /* Package@swift-4.2.swift */,
|
||||
8055B0521E201D24009DC3EE /* Sources */,
|
||||
80B28EB11E201F72007E3A77 /* Playgrounds */,
|
||||
8055B0571E201DF3009DC3EE /* Tests */,
|
||||
80B28EB31E201F81007E3A77 /* Supporting Files */,
|
||||
804879361E217C7700AD31A3 /* Scripts */,
|
||||
8055B0391E201C5D009DC3EE /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
@@ -133,10 +158,7 @@
|
||||
8055B0521E201D24009DC3EE /* Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
80E8E21C1E20301E00395E49 /* OrderedDictionary.swift */,
|
||||
80A203A01F3F483700622481 /* OrderedDictionary+Codable.swift */,
|
||||
80E8E22F1E2133D100395E49 /* OrderedDictionary+Description.swift */,
|
||||
8048C8AA22D8911B0086B88B /* OrderedDictionary+Deprecated.swift */,
|
||||
80B7BC2B261A65C100EB2CA2 /* OrderedDictionary */,
|
||||
);
|
||||
path = Sources;
|
||||
sourceTree = "<group>";
|
||||
@@ -144,18 +166,29 @@
|
||||
8055B0571E201DF3009DC3EE /* Tests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8055B0581E201DF3009DC3EE /* OrderedDictionaryTests.swift */,
|
||||
80AF1630252A48080065B656 /* OrderedDictionaryTests */,
|
||||
);
|
||||
path = Tests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
80B28EB11E201F72007E3A77 /* Playgrounds */ = {
|
||||
80AF1630252A48080065B656 /* OrderedDictionaryTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
80B28EB71E2020DD007E3A77 /* OrderedDictionary.playground */,
|
||||
807AA68F1F1E587A00576474 /* OrderedDictionary+Codable.playground */,
|
||||
80BFD01A252B049E002B3C05 /* InitializationTests.swift */,
|
||||
80BFD06F252B0C16002B3C05 /* AccessTests.swift */,
|
||||
80BFD0A7252B1C96002B3C05 /* InsertionsTests.swift */,
|
||||
80BFD07D252B0E19002B3C05 /* UpdatesTests.swift */,
|
||||
80BFD060252B0A7F002B3C05 /* RemovalTests.swift */,
|
||||
80BE57A4252B3F6000F85D45 /* SubscriptAmbiguityTests.swift */,
|
||||
80BFD076252B0D94002B3C05 /* MapFilterTests.swift */,
|
||||
80BFD051252B09DC002B3C05 /* ReorderingTests.swift */,
|
||||
80BFD029252B069F002B3C05 /* SortingTests.swift */,
|
||||
80BFD04A252B0951002B3C05 /* CapacityTests.swift */,
|
||||
80BFD03F252B0740002B3C05 /* CodingTests.swift */,
|
||||
80BFD030252B06EB002B3C05 /* DescriptionTests.swift */,
|
||||
80BE5799252B278D00F85D45 /* XCTUnwrapShim.swift */,
|
||||
);
|
||||
path = Playgrounds;
|
||||
path = OrderedDictionaryTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
80B28EB31E201F81007E3A77 /* Supporting Files */ = {
|
||||
@@ -167,6 +200,19 @@
|
||||
path = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
80B7BC2B261A65C100EB2CA2 /* OrderedDictionary */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
80E8E21C1E20301E00395E49 /* OrderedDictionary.swift */,
|
||||
80A203A01F3F483700622481 /* OrderedDictionary+Codable.swift */,
|
||||
80E8E22F1E2133D100395E49 /* OrderedDictionary+Description.swift */,
|
||||
8048C8AA22D8911B0086B88B /* OrderedDictionary+Deprecated.swift */,
|
||||
80BE57D3252BAB9400F85D45 /* OrderedDictionarySlice.swift */,
|
||||
80BE57DE252BADEE00F85D45 /* Dictionary+OrderedDictionary.swift */,
|
||||
);
|
||||
path = OrderedDictionary;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
@@ -266,7 +312,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0820;
|
||||
LastUpgradeCheck = 1020;
|
||||
LastUpgradeCheck = 1320;
|
||||
ORGANIZATIONNAME = "Lukas Kubanek";
|
||||
TargetAttributes = {
|
||||
8055B0371E201C5D009DC3EE = {
|
||||
@@ -351,7 +397,9 @@
|
||||
8048C8AB22D8911B0086B88B /* OrderedDictionary+Deprecated.swift in Sources */,
|
||||
80E8E2301E2133D100395E49 /* OrderedDictionary+Description.swift in Sources */,
|
||||
80E8E21D1E20301E00395E49 /* OrderedDictionary.swift in Sources */,
|
||||
80BE57D4252BAB9400F85D45 /* OrderedDictionarySlice.swift in Sources */,
|
||||
80A203A11F3F483700622481 /* OrderedDictionary+Codable.swift in Sources */,
|
||||
80BE57DF252BADEE00F85D45 /* Dictionary+OrderedDictionary.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -359,7 +407,19 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8055B0591E201DF3009DC3EE /* OrderedDictionaryTests.swift in Sources */,
|
||||
80BFD061252B0A7F002B3C05 /* RemovalTests.swift in Sources */,
|
||||
80BFD031252B06EB002B3C05 /* DescriptionTests.swift in Sources */,
|
||||
80BFD077252B0D94002B3C05 /* MapFilterTests.swift in Sources */,
|
||||
80BFD070252B0C16002B3C05 /* AccessTests.swift in Sources */,
|
||||
80BFD052252B09DC002B3C05 /* ReorderingTests.swift in Sources */,
|
||||
80BFD04B252B0951002B3C05 /* CapacityTests.swift in Sources */,
|
||||
80BFD02A252B069F002B3C05 /* SortingTests.swift in Sources */,
|
||||
80BFD07E252B0E19002B3C05 /* UpdatesTests.swift in Sources */,
|
||||
80BFD0A8252B1C96002B3C05 /* InsertionsTests.swift in Sources */,
|
||||
80BFD040252B0740002B3C05 /* CodingTests.swift in Sources */,
|
||||
80BE57A5252B3F6000F85D45 /* SubscriptAmbiguityTests.swift in Sources */,
|
||||
80BFD01B252B049E002B3C05 /* InitializationTests.swift in Sources */,
|
||||
80BE579A252B278D00F85D45 /* XCTUnwrapShim.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -370,7 +430,9 @@
|
||||
8048C8AC22D8911B0086B88B /* OrderedDictionary+Deprecated.swift in Sources */,
|
||||
80E8E2311E2133D100395E49 /* OrderedDictionary+Description.swift in Sources */,
|
||||
80E8E21F1E20425B00395E49 /* OrderedDictionary.swift in Sources */,
|
||||
80BE57D5252BAB9400F85D45 /* OrderedDictionarySlice.swift in Sources */,
|
||||
80A203A21F3F4C1F00622481 /* OrderedDictionary+Codable.swift in Sources */,
|
||||
80BE57E0252BADEE00F85D45 /* Dictionary+OrderedDictionary.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -378,7 +440,19 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
80B28EB01E201F1C007E3A77 /* OrderedDictionaryTests.swift in Sources */,
|
||||
80BFD062252B0A7F002B3C05 /* RemovalTests.swift in Sources */,
|
||||
80BFD032252B06EB002B3C05 /* DescriptionTests.swift in Sources */,
|
||||
80BFD078252B0D94002B3C05 /* MapFilterTests.swift in Sources */,
|
||||
80BFD071252B0C16002B3C05 /* AccessTests.swift in Sources */,
|
||||
80BFD053252B09DC002B3C05 /* ReorderingTests.swift in Sources */,
|
||||
80BFD04C252B0951002B3C05 /* CapacityTests.swift in Sources */,
|
||||
80BFD02B252B069F002B3C05 /* SortingTests.swift in Sources */,
|
||||
80BFD07F252B0E19002B3C05 /* UpdatesTests.swift in Sources */,
|
||||
80BFD0A9252B1C96002B3C05 /* InsertionsTests.swift in Sources */,
|
||||
80BFD041252B0740002B3C05 /* CodingTests.swift in Sources */,
|
||||
80BE57A6252B3F6000F85D45 /* SubscriptAmbiguityTests.swift in Sources */,
|
||||
80BFD01C252B049E002B3C05 /* InitializationTests.swift in Sources */,
|
||||
80BE579B252B278D00F85D45 /* XCTUnwrapShim.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -423,6 +497,7 @@
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
@@ -456,7 +531,7 @@
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
@@ -487,6 +562,7 @@
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
@@ -512,7 +588,7 @@
|
||||
SDKROOT = macosx;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
@@ -533,10 +609,10 @@
|
||||
INFOPLIST_FILE = "Supporting Files/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||
MARKETING_VERSION = 4.0.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lukaskubanek.OrderedDictionary;
|
||||
PRODUCT_NAME = OrderedDictionary;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 4.2;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -555,10 +631,10 @@
|
||||
INFOPLIST_FILE = "Supporting Files/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||
MARKETING_VERSION = 4.0.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lukaskubanek.OrderedDictionary;
|
||||
PRODUCT_NAME = OrderedDictionary;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 4.2;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -569,6 +645,7 @@
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = "Supporting Files/Info-Tests.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lukaskubanek.OrderedDictionaryTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
};
|
||||
@@ -581,6 +658,7 @@
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = "Supporting Files/Info-Tests.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lukaskubanek.OrderedDictionaryTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
};
|
||||
@@ -600,11 +678,11 @@
|
||||
INFOPLIST_FILE = "Supporting Files/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
MARKETING_VERSION = 4.0.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lukaskubanek.OrderedDictionary;
|
||||
PRODUCT_NAME = OrderedDictionary;
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 4.2;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -623,11 +701,11 @@
|
||||
INFOPLIST_FILE = "Supporting Files/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
MARKETING_VERSION = 4.0.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lukaskubanek.OrderedDictionary;
|
||||
PRODUCT_NAME = OrderedDictionary;
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 4.2;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1140"
|
||||
LastUpgradeVersion = "1320"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -26,16 +26,8 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8055B0371E201C5D009DC3EE"
|
||||
BuildableName = "OrderedDictionary.framework"
|
||||
BlueprintName = "OrderedDictionary-Mac"
|
||||
ReferencedContainer = "container:OrderedDictionary.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
codeCoverageEnabled = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1140"
|
||||
LastUpgradeVersion = "1320"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -27,15 +27,6 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "80B28E961E201EC8007E3A77"
|
||||
BuildableName = "OrderedDictionary.framework"
|
||||
BlueprintName = "OrderedDictionary-iOS"
|
||||
ReferencedContainer = "container:OrderedDictionary.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
|
||||
+2
-4
@@ -14,13 +14,11 @@ let package = Package(
|
||||
targets: [
|
||||
.target(
|
||||
name: "OrderedDictionary",
|
||||
dependencies: [],
|
||||
path: "Sources"
|
||||
dependencies: []
|
||||
),
|
||||
.testTarget(
|
||||
name: "OrderedDictionaryTests",
|
||||
dependencies: ["OrderedDictionary"],
|
||||
path: "Tests"
|
||||
dependencies: ["OrderedDictionary"]
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
// swift-tools-version:4.2
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "OrderedDictionary",
|
||||
products: [
|
||||
.library(
|
||||
name: "OrderedDictionary",
|
||||
targets: ["OrderedDictionary"]
|
||||
)
|
||||
],
|
||||
dependencies: [],
|
||||
targets: [
|
||||
.target(
|
||||
name: "OrderedDictionary",
|
||||
dependencies: [],
|
||||
path: "Sources"
|
||||
),
|
||||
.testTarget(
|
||||
name: "OrderedDictionaryTests",
|
||||
dependencies: ["OrderedDictionary"],
|
||||
path: "Tests"
|
||||
)
|
||||
],
|
||||
swiftLanguageVersions: [.v4_2]
|
||||
)
|
||||
@@ -1,33 +0,0 @@
|
||||
import Foundation
|
||||
import OrderedDictionary
|
||||
|
||||
let orderedDictionary1: OrderedDictionary<String, Int> = [
|
||||
"A": 42,
|
||||
"B": 100,
|
||||
"C": 11
|
||||
]
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - JSON Encoding & Decoding
|
||||
// ======================================================= //
|
||||
|
||||
let jsonEncoder = JSONEncoder()
|
||||
let jsonData = try! jsonEncoder.encode(orderedDictionary1)
|
||||
let jsonString = String(data: jsonData, encoding: .utf8)
|
||||
|
||||
let jsonDecoder = JSONDecoder()
|
||||
let orderedDictionary2 = try! jsonDecoder.decode(OrderedDictionary<String, Int>.self, from: jsonData)
|
||||
|
||||
orderedDictionary1 == orderedDictionary2
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Property List Encoding & Decoding
|
||||
// ======================================================= //
|
||||
|
||||
let plistEncoder = PropertyListEncoder()
|
||||
let plistData = try! plistEncoder.encode(orderedDictionary1)
|
||||
|
||||
let plistDecoder = PropertyListDecoder()
|
||||
let orderedDictionary3 = try! plistDecoder.decode(OrderedDictionary<String, Int>.self, from: plistData)
|
||||
|
||||
orderedDictionary1 == orderedDictionary3
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<playground version='5.0' target-platform='macos'>
|
||||
<timeline fileName='timeline.xctimeline'/>
|
||||
</playground>
|
||||
@@ -1,134 +0,0 @@
|
||||
import OrderedDictionary
|
||||
|
||||
// ======================================================= //
|
||||
// Helpers
|
||||
// ======================================================= //
|
||||
|
||||
func print(_ optional: Any?) {
|
||||
print(optional as Any)
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// Construction
|
||||
// ======================================================= //
|
||||
|
||||
// Construct an ordered dictionary using a dictionary literal
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3, "D": 4]
|
||||
|
||||
// Output the ordered dictionary
|
||||
print(orderedDictionary) // => [A: 1, B: 2, C: 3, D: 4]
|
||||
|
||||
// ======================================================= //
|
||||
// Enumeration
|
||||
// ======================================================= //
|
||||
|
||||
// Loop through the ordered dictionary
|
||||
for (key, value) in orderedDictionary {
|
||||
print("[\(key): \(value)]") // => [A: 1], => [B: 2], => [C: 3], => [D: 4]
|
||||
}
|
||||
|
||||
// Loop through the ordered dictionary with an additional index
|
||||
for (index, element) in orderedDictionary.enumerated() {
|
||||
print("(\(index): [\(element.key): \(element.value)])") // => (0: [A: 1]), => (1: [B: 2]), => (2: [C: 3]), => (3: [D: 4])
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// Acessing Content
|
||||
// ======================================================= //
|
||||
|
||||
// Access the value for an existing key using the subscript
|
||||
print(orderedDictionary["A"]) // => Optional(1)
|
||||
|
||||
// Access the value for a non-existent key using the subscript
|
||||
print(orderedDictionary["X"]) // => nil
|
||||
|
||||
// Access the value for a key using the method
|
||||
print(orderedDictionary.value(forKey: "A")) // => Optional(1)
|
||||
|
||||
// Access the value for a non-existent key using the method
|
||||
print(orderedDictionary.value(forKey: "X")) // => nil
|
||||
|
||||
// Access the key-value pair (element) at an existing index
|
||||
print(orderedDictionary[2]) // => ("C", 3)
|
||||
|
||||
// Access the key-value pair (element) at a non-existent index
|
||||
//print(orderedDictionary[10]) // => fatal error
|
||||
|
||||
// Get the index for an existing key
|
||||
print(orderedDictionary.index(forKey: "D")) // => Optional(3)
|
||||
|
||||
// ======================================================= //
|
||||
// Modifying Content Using Keys
|
||||
// ======================================================= //
|
||||
|
||||
// Modify the value for an existing key using the subscript
|
||||
orderedDictionary["A"] = 100
|
||||
print(orderedDictionary["A"]) // => Optional(100)
|
||||
|
||||
// Modify the value for an existing key using the method
|
||||
orderedDictionary.updateValue(42, forKey: "D")
|
||||
print(orderedDictionary["D"]) // => Optional(42)
|
||||
|
||||
// Append a new key-value pair by setting a value for an non-existent key
|
||||
orderedDictionary["E"] = 5
|
||||
print(orderedDictionary) // => [A: 100, B: 2, C: 3, D: 42, E: 5]
|
||||
|
||||
// Remove a key-value pair by setting `nil` value for an existing key
|
||||
orderedDictionary["B"] = nil
|
||||
print(orderedDictionary["B"]) // => nil
|
||||
print(orderedDictionary) // => [A: 100, C: 3, D: 42, E: 5]
|
||||
|
||||
// ======================================================= //
|
||||
// Modifying Content Using Indexes
|
||||
// ======================================================= //
|
||||
|
||||
// Modify an element at an existing index
|
||||
let replacedElement = orderedDictionary.update(("F", 235), at: 2)
|
||||
print(orderedDictionary[2]) // => ("F", 235)
|
||||
print(orderedDictionary) // => [A: 100, C: 3, F: 235, E: 5]
|
||||
print(replacedElement) // => Optional("D", 42)
|
||||
|
||||
// Modify an element at a non-existent index
|
||||
//orderedDictionary.update(("L", 0), at: 100) // => fatal error
|
||||
|
||||
// ======================================================= //
|
||||
// Sorting
|
||||
// ======================================================= //
|
||||
|
||||
// Sort the ordered dictionary using a closure
|
||||
orderedDictionary.sort {
|
||||
if $0.value == $1.value {
|
||||
return $0.key < $1.key
|
||||
} else {
|
||||
return $0.value < $1.value
|
||||
}
|
||||
}
|
||||
|
||||
print(orderedDictionary) // => [C: 3, E: 5, A: 100, F: 235]
|
||||
|
||||
// ======================================================= //
|
||||
// Removing Content
|
||||
// ======================================================= //
|
||||
|
||||
// Remove value for an existing key
|
||||
let removedValue = orderedDictionary.removeValue(forKey: "F")
|
||||
print(removedValue) // => Optional(235)
|
||||
print(orderedDictionary["F"]) // => nil
|
||||
print(orderedDictionary) // => [C: 3, E: 5, A: 100]
|
||||
|
||||
// Remove value for a non-existent key
|
||||
orderedDictionary.removeValue(forKey: "X")
|
||||
print(orderedDictionary) // => [C: 3, E: 5, A: 100]
|
||||
|
||||
// Remove element at an existing index
|
||||
let removedElement = orderedDictionary.remove(at: 1)
|
||||
print(removedElement) // => Optional((E, 5))
|
||||
print(orderedDictionary) // => [C: 3, A: 100]
|
||||
|
||||
// Remove element at a non-existent index
|
||||
orderedDictionary.remove(at: 42)
|
||||
print(orderedDictionary) // => [C: 3, A: 100]
|
||||
|
||||
// Remove all elements
|
||||
orderedDictionary.removeAll()
|
||||
print(orderedDictionary) // => [:]
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<playground version='5.0' target-platform='osx'>
|
||||
<timeline fileName='timeline.xctimeline'/>
|
||||
</playground>
|
||||
@@ -5,7 +5,7 @@
|
||||
<img src="https://img.shields.io/github/release/lukaskubanek/OrderedDictionary/all.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://developer.apple.com/swift">
|
||||
<img src="https://img.shields.io/badge/Swift-4.2+-orange.svg?style=flat-square" alt="Swift 4.2+">
|
||||
<img src="https://img.shields.io/badge/Swift-5.0-orange.svg?style=flat-square" alt="Swift 5.0">
|
||||
</a>
|
||||
<a href="https://swift.org/package-manager">
|
||||
<img src="https://img.shields.io/badge/SPM-compatible-brightgreen.svg?style=flat-square" alt="Swift Package Manager">
|
||||
@@ -23,20 +23,21 @@
|
||||
|
||||
OrderedDictionary is a lightweight implementation of an ordered dictionary data structure in Swift.
|
||||
|
||||
The `OrderedDictionary` struct is a generic collection that combines the features of `Dictionary` and `Array` data structures from the Swift standard library. Like `Dictionary`, it stores key-value pairs with each key being unique and maps each key to an associated value. Like `Array`, it stores those pairs sorted and accessible by a zero-based integer index.
|
||||
The `OrderedDictionary<Key: Hashable, Value>` struct is a generic collection that combines the features of the `Dictionary` and `Array` data structures from the Swift standard library. Like `Dictionary`, it stores key-value pairs with each key being unique and maps each key to an associated value. Like `Array`, it stores those pairs sorted and accessible by a zero-based integer index.
|
||||
|
||||
`OrderedDictionary` provides similar APIs to collections in the Swift standard library like accessing contents by keys or indices, inserting and removing elements, iterating, sorting, filtering, etc.
|
||||
`OrderedDictionary` provides similar APIs to collections available in the Swift standard library like accessing contents by keys or indices, inserting and removing elements, iterating, sorting, transforming, filtering, etc.
|
||||
|
||||
Internally, `OrderedDictionary` uses a backing storage composed of a `Dictionary` for storing the key-value pairs and an `Array` for managing the ordered keys. This architecture makes it not the most pefromant implementation possible, but it gets its job done while reusing most functionality from the Swift standard library.
|
||||
Internally, `OrderedDictionary` uses backing storage composed of a `Dictionary` for storing the key-value pairs and an `Array` for managing the ordered keys. This is certainly not the most performant implementation one can achieve, but it gets its job done while reusing most functionality from the Swift standard library.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Swift 4.2+
|
||||
- Xcode 10.0+
|
||||
- iOS 8.0+ / macOS 10.10+
|
||||
- Swift 5.0 or later
|
||||
- Xcode 11 or later
|
||||
- iOS 8 or later / macOS 10.10 or later
|
||||
|
||||
*For support of Swift 4.0 and 4.1 please refer to version 2.x of this library.*
|
||||
*The Xcode and OS requirements apply only when the library is integrated as a framework or via the Xcode project.*
|
||||
*For support of older Swift versions, please refer to older versions of this library. For Swift 4.2, use version 3.x, and for Swift 4.0-4.1, use version 2.x.*
|
||||
|
||||
*The requirements for Xcode and OS versions only apply when the library is integrated as a framework or via the Xcode project.*
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -48,7 +49,7 @@ To install OrderedDictionary using the [Swift Package Manager](https://swift.org
|
||||
let package = Package(
|
||||
...
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/lukaskubanek/OrderedDictionary.git", from: "3.0.0")
|
||||
.package(url: "https://github.com/lukaskubanek/OrderedDictionary.git", from: "4.0.0")
|
||||
],
|
||||
...
|
||||
)
|
||||
@@ -59,7 +60,7 @@ let package = Package(
|
||||
To install OrderedDictionary using [Carthage](https://github.com/Carthage/Carthage), add it as a dependency into your `Cartfile`:
|
||||
|
||||
```plain
|
||||
github "lukaskubanek/LoremSwiftum"
|
||||
github "lukaskubanek/OrderedDictionary" ~> 4.0
|
||||
```
|
||||
|
||||
Then drag either the `OrderedDictionary.xcodeproj` or the `OrderedDictionary.framework` into your Xcode project/workspace and link your target against the `OrderedDictionary.framework`. Make sure that the framework [gets copied](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application) to your application bundle.
|
||||
@@ -68,10 +69,18 @@ Then drag either the `OrderedDictionary.xcodeproj` or the `OrderedDictionary.fra
|
||||
|
||||
You can also install OrderedDictionary via [Git submodules](http://git-scm.com/book/en/v2/Git-Tools-Submodules) and integrate the project `OrderedDictionary.xcodeproj` from the submodule directly into your Xcode workspace.
|
||||
|
||||
### ⚠️ Note About CocoaPods
|
||||
### Note About CocoaPods
|
||||
|
||||
Although there has been a high demand for [CocoaPods](https://cocoapods.org) support, this distribution method won't be officially supported by this library. If you really want to integrate this library via CocoaPods, you can create and maintain a custom podspec (see the last section of [this post](https://guides.cocoapods.org/syntax/podfile.html#pod)).
|
||||
|
||||
## Usage & Docs
|
||||
|
||||
For the usage of this library please refer to [the example playground](Playgrounds/OrderedDictionary.playground/Contents.swift). For documentation please refer to [the documentation comments](Sources/OrderedDictionary.swift).
|
||||
For the explanation of the API of `OrderedDictionary` and examples on how to use this data structure, please refer to the [documentation comments](Sources/OrderedDictionary/OrderedDictionary.swift) or the [comprehensive test suite](Tests/OrderedDictionaryTests).
|
||||
|
||||
## Changelog
|
||||
|
||||
To view the changelog, refer to GitHub's [releases page](https://github.com/lukaskubanek/OrderedDictionary/releases). If you're upgrading from version 3.x, you might want to check out the list of changes made in [version 4.0](https://github.com/lukaskubanek/OrderedDictionary/releases/tag/v4.0.0).
|
||||
|
||||
## Credits
|
||||
|
||||
OrderedDictionary was built by [@lukaskubanek](https://twitter.com/lukaskubanek), the founder and developer of [Diagrams](https://diagrams.app), a native diagram editor for Mac. This data structure is being used extensively for powering Diagrams' data model.
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
extension OrderedDictionary {
|
||||
|
||||
#if swift(>=5.0)
|
||||
@available(*, deprecated, message: "Please use init(values:uniquelyKeyedBy:).", renamed: "init(values:uniquelyKeyedBy:)")
|
||||
public init<S: Sequence>(
|
||||
values: S,
|
||||
keyedBy extractKey: (Value) -> Key
|
||||
) where S.Element == Value {
|
||||
self.init(values: values, uniquelyKeyedBy: extractKey)
|
||||
}
|
||||
#endif
|
||||
|
||||
@available(*, deprecated, message: "Please use init(values:uniquelyKeyedBy:).", renamed: "init(values:uniquelyKeyedBy:)")
|
||||
public init(
|
||||
values: [Value],
|
||||
keyedBy keyPath: KeyPath<Value, Key>
|
||||
) {
|
||||
self.init(values: values, uniquelyKeyedBy: keyPath)
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "Please use init(uniqueKeysWithValues:).", renamed: "init(uniqueKeysWithValues:)")
|
||||
public init<S: Sequence>(_ elements: S) where S.Element == Element {
|
||||
self.init(uniqueKeysWithValues: elements)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,736 +0,0 @@
|
||||
/// A generic collection for storing key-value pairs in an ordered manner.
|
||||
///
|
||||
/// Same as in a dictionary all keys in the collection are unique and have an associated value.
|
||||
/// Same as in an array, all key-value pairs (elements) are kept sorted and accessible by
|
||||
/// a zero-based integer index.
|
||||
public struct OrderedDictionary<Key: Hashable, Value>: BidirectionalCollection {
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Type Aliases
|
||||
// ======================================================= //
|
||||
|
||||
/// The type of the key-value pair stored in the ordered dictionary.
|
||||
public typealias Element = (key: Key, value: Value)
|
||||
|
||||
/// The type of the index.
|
||||
public typealias Index = Int
|
||||
|
||||
/// The type of the indices collection.
|
||||
public typealias Indices = CountableRange<Int>
|
||||
|
||||
/// The type of the contiguous subrange of the ordered dictionary's elements.
|
||||
///
|
||||
/// - SeeAlso: OrderedDictionarySlice
|
||||
public typealias SubSequence = OrderedDictionarySlice<Key, Value>
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Initialization
|
||||
// ======================================================= //
|
||||
|
||||
/// Initializes an empty ordered dictionary.
|
||||
public init() {
|
||||
self._orderedKeys = [Key]()
|
||||
self._keysToValues = [Key: Value]()
|
||||
}
|
||||
|
||||
/// Initializes an empty ordered dictionary with preallocated space for at least the specified
|
||||
/// number of elements.
|
||||
public init(minimumCapacity: Int) {
|
||||
self.init()
|
||||
self.reserveCapacity(minimumCapacity)
|
||||
}
|
||||
|
||||
/// Initializes an ordered dictionary from a regular unsorted dictionary by sorting it using
|
||||
/// the given sort function.
|
||||
///
|
||||
/// - Parameter unsorted: The unsorted dictionary.
|
||||
/// - Parameter areInIncreasingOrder: The sort function which compares the key-value pairs.
|
||||
public init(
|
||||
unsorted: Dictionary<Key, Value>,
|
||||
areInIncreasingOrder: (Element, Element) throws -> Bool
|
||||
) rethrows {
|
||||
let keysAndValues = try Array(unsorted).sorted(by: areInIncreasingOrder)
|
||||
|
||||
self.init(
|
||||
uniqueKeysWithValues: keysAndValues,
|
||||
minimumCapacity: unsorted.count
|
||||
)
|
||||
}
|
||||
|
||||
/// Initializes an ordered dictionary from a sequence of values keyed by a unique key extracted
|
||||
/// from the value using the given closure.
|
||||
///
|
||||
/// - Parameter values: The sequence of values.
|
||||
/// - Parameter extractKey: The closure which extracts a key from the value. The returned keys
|
||||
/// must be unique for all values from the sequence.
|
||||
public init<S: Sequence>(
|
||||
values: S,
|
||||
uniquelyKeyedBy extractKey: (Value) throws -> Key
|
||||
) rethrows where S.Element == Value {
|
||||
self.init(uniqueKeysWithValues: try values.map { value in
|
||||
return (try extractKey(value), value)
|
||||
})
|
||||
}
|
||||
|
||||
/// Initializes an ordered dictionary from a sequence of values keyed by a unique key extracted
|
||||
/// from the value using the given key path.
|
||||
///
|
||||
/// - Parameter values: The sequence of values.
|
||||
/// - Parameter keyPath: The key path to use for extracting a key from the value. The extracted
|
||||
/// keys must be unique for all values from the sequence.
|
||||
public init<S: Sequence>(
|
||||
values: S,
|
||||
uniquelyKeyedBy keyPath: KeyPath<Value, Key>
|
||||
) where S.Element == Value {
|
||||
self.init(uniqueKeysWithValues: values.map { value in
|
||||
return (value[keyPath: keyPath], value)
|
||||
})
|
||||
}
|
||||
|
||||
/// Initializes an ordered dictionary from a sequence of key-value pairs.
|
||||
///
|
||||
/// - Parameter keysAndValues: A sequence of key-value pairs to use for the new ordered
|
||||
/// dictionary. Every key in `keysAndValues` must be unique.
|
||||
public init<S: Sequence>(
|
||||
uniqueKeysWithValues keysAndValues: S
|
||||
) where S.Element == Element {
|
||||
self.init(
|
||||
uniqueKeysWithValues: keysAndValues,
|
||||
minimumCapacity: keysAndValues.underestimatedCount
|
||||
)
|
||||
}
|
||||
|
||||
private init<S: Sequence>(
|
||||
uniqueKeysWithValues keysAndValues: S,
|
||||
minimumCapacity: Int
|
||||
) where S.Element == Element {
|
||||
self.init(minimumCapacity: minimumCapacity)
|
||||
|
||||
for (key, value) in keysAndValues {
|
||||
precondition(!containsKey(key), "Sequence of key-value pairs contains duplicate keys")
|
||||
self[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Ordered Keys & Values
|
||||
// ======================================================= //
|
||||
|
||||
/// A collection containing just the keys of the ordered dictionary in the correct order.
|
||||
public var orderedKeys: OrderedDictionaryKeys<Key, Value> {
|
||||
return self.lazy.map { $0.key }
|
||||
}
|
||||
|
||||
/// A collection containing just the values of the ordered dictionary in the correct order.
|
||||
public var orderedValues: OrderedDictionaryValues<Key, Value> {
|
||||
return self.lazy.map { $0.value }
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Dictionary
|
||||
// ======================================================= //
|
||||
|
||||
/// Converts itself to a common unsorted dictionary.
|
||||
public var unorderedDictionary: Dictionary<Key, Value> {
|
||||
return _keysToValues
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Indices
|
||||
// ======================================================= //
|
||||
|
||||
/// The indices that are valid for subscripting the ordered dictionary.
|
||||
public var indices: Indices {
|
||||
return _orderedKeys.indices
|
||||
}
|
||||
|
||||
/// The position of the first key-value pair in a non-empty ordered dictionary.
|
||||
public var startIndex: Index {
|
||||
return _orderedKeys.startIndex
|
||||
}
|
||||
|
||||
/// The position which is one greater than the position of the last valid key-value pair in the
|
||||
/// ordered dictionary.
|
||||
public var endIndex: Index {
|
||||
return _orderedKeys.endIndex
|
||||
}
|
||||
|
||||
/// Returns the position immediately after the given index.
|
||||
public func index(after i: Index) -> Index {
|
||||
return _orderedKeys.index(after: i)
|
||||
}
|
||||
|
||||
/// Returns the position immediately before the given index.
|
||||
public func index(before i: Index) -> Index {
|
||||
return _orderedKeys.index(before: i)
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Slices
|
||||
// ======================================================= //
|
||||
|
||||
/// Accesses a contiguous subrange of the ordered dictionary.
|
||||
///
|
||||
/// - Parameter bounds: A range of the ordered dictionary's indices. The bounds of the range
|
||||
/// must be valid indices of the ordered dictionary.
|
||||
/// - Returns: The slice view at the ordered dictionary in the specified subrange.
|
||||
public subscript(bounds: Range<Index>) -> SubSequence {
|
||||
return OrderedDictionarySlice(base: self, bounds: bounds)
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Key-based Access
|
||||
// ======================================================= //
|
||||
|
||||
/// Accesses the value associated with the given key for reading and writing.
|
||||
///
|
||||
/// This key-based subscript returns the value for the given key if the key is found in the
|
||||
/// ordered dictionary, or `nil` if the key is not found.
|
||||
///
|
||||
/// When you assign a value for a key and that key already exists, the ordered dictionary
|
||||
/// overwrites the existing value and preservers the index of the key-value pair. If the ordered
|
||||
/// dictionary does not contain the key, a new key-value pair is appended to the end of the
|
||||
/// ordered dictionary.
|
||||
///
|
||||
/// If you assign `nil` as the value for the given key, the ordered dictionary removes that key
|
||||
/// and its associated value if it exists.
|
||||
///
|
||||
/// - Parameter key: The key to find in the ordered dictionary.
|
||||
/// - Returns: The value associated with `key` if `key` is in the ordered dictionary; otherwise,
|
||||
/// `nil`.
|
||||
public subscript(key: Key) -> Value? {
|
||||
get {
|
||||
return value(forKey: key)
|
||||
}
|
||||
set(newValue) {
|
||||
if let newValue = newValue {
|
||||
updateValue(newValue, forKey: key)
|
||||
} else {
|
||||
removeValue(forKey: key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a Boolean value indicating whether the ordered dictionary contains the given key.
|
||||
///
|
||||
/// - Parameter key: The key to be looked up.
|
||||
/// - Returns: `true` if the ordered dictionary contains the given key; otherwise, `false`.
|
||||
public func containsKey(_ key: Key) -> Bool {
|
||||
return _keysToValues[key] != nil
|
||||
}
|
||||
|
||||
/// Returns the value associated with the given key if the key is found in the ordered
|
||||
/// dictionary, or `nil` if the key is not found.
|
||||
///
|
||||
/// - Parameter key: The key to find in the ordered dictionary.
|
||||
/// - Returns: The value associated with `key` if `key` is in the ordered dictionary; otherwise,
|
||||
/// `nil`.
|
||||
public func value(forKey key: Key) -> Value? {
|
||||
return _keysToValues[key]
|
||||
}
|
||||
|
||||
/// Updates the value stored in the ordered dictionary for the given key, or appends a new
|
||||
/// key-value pair if the key does not exist.
|
||||
///
|
||||
/// - Parameter value: The new value to add to the ordered dictionary.
|
||||
/// - Parameter key: The key to associate with `value`. If `key` already exists in the ordered
|
||||
/// dictionary, `value` replaces the existing associated value. If `key` is not already a key
|
||||
/// of the ordered dictionary, the `(key, value)` pair is appended at the end of the ordered
|
||||
/// dictionary.
|
||||
@discardableResult
|
||||
public mutating func updateValue(_ value: Value, forKey key: Key) -> Value? {
|
||||
if containsKey(key) {
|
||||
let currentValue = _unsafeValue(forKey: key)
|
||||
|
||||
_keysToValues[key] = value
|
||||
|
||||
return currentValue
|
||||
} else {
|
||||
_orderedKeys.append(key)
|
||||
_keysToValues[key] = value
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the given key and its associated value from the ordered dictionary.
|
||||
///
|
||||
/// If the key is found in the ordered dictionary, this method returns the key's associated
|
||||
/// value. On removal, the indices of the ordered dictionary are invalidated. If the key is
|
||||
/// not found in the ordered dictionary, this method returns `nil`.
|
||||
///
|
||||
/// - Parameter key: The key to remove along with its associated value.
|
||||
/// - Returns: The value that was removed, or `nil` if the key was not present in the
|
||||
/// ordered dictionary.
|
||||
///
|
||||
/// - SeeAlso: remove(at:)
|
||||
@discardableResult
|
||||
public mutating func removeValue(forKey key: Key) -> Value? {
|
||||
guard let index = index(forKey: key) else { return nil }
|
||||
|
||||
let currentValue = self[index].value
|
||||
|
||||
_orderedKeys.remove(at: index)
|
||||
_keysToValues[key] = nil
|
||||
|
||||
return currentValue
|
||||
}
|
||||
|
||||
/// Removes all key-value pairs from the ordered dictionary and invalidates all indices.
|
||||
///
|
||||
/// - Parameter keepCapacity: Whether the ordered dictionary should keep its underlying storage.
|
||||
/// If you pass `true`, the operation preserves the storage capacity that the collection has,
|
||||
/// otherwise the underlying storage is released. The default is `false`.
|
||||
public mutating func removeAll(keepingCapacity keepCapacity: Bool = false) {
|
||||
_orderedKeys.removeAll(keepingCapacity: keepCapacity)
|
||||
_keysToValues.removeAll(keepingCapacity: keepCapacity)
|
||||
}
|
||||
|
||||
private func _unsafeValue(forKey key: Key) -> Value {
|
||||
let value = _keysToValues[key]
|
||||
precondition(value != nil, "Inconsistency error occurred in OrderedDictionary")
|
||||
return value!
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Index-based Access
|
||||
// ======================================================= //
|
||||
|
||||
/// Accesses the key-value pair at the specified position.
|
||||
///
|
||||
/// The specified position has to be a valid index of the ordered dictionary. The index-base
|
||||
/// subscript returns the key-value pair corresponding to the index.
|
||||
///
|
||||
/// - Parameter position: The position of the key-value pair to access. `position` must be
|
||||
/// a valid index of the ordered dictionary and not equal to `endIndex`.
|
||||
/// - Returns: A tuple containing the key-value pair corresponding to `position`.
|
||||
///
|
||||
/// - SeeAlso: update(:at:)
|
||||
public subscript(position: Index) -> Element {
|
||||
precondition(indices.contains(position), "OrderedDictionary index is out of range")
|
||||
|
||||
let key = _orderedKeys[position]
|
||||
let value = _unsafeValue(forKey: key)
|
||||
|
||||
return (key, value)
|
||||
}
|
||||
|
||||
/// Returns the index for the given key.
|
||||
///
|
||||
/// - Parameter key: The key to find in the ordered dictionary.
|
||||
/// - Returns: The index for `key` and its associated value if `key` is in the ordered
|
||||
/// dictionary; otherwise, `nil`.
|
||||
public func index(forKey key: Key) -> Index? {
|
||||
#if swift(>=5.0)
|
||||
return _orderedKeys.firstIndex(of: key)
|
||||
#else
|
||||
return _orderedKeys.index(of: key)
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Returns the key-value pair at the specified index, or `nil` if there is no key-value pair
|
||||
/// at that index.
|
||||
///
|
||||
/// - Parameter index: The index of the key-value pair to be looked up. `index` does not have to
|
||||
/// be a valid index.
|
||||
/// - Returns: A tuple containing the key-value pair corresponding to `index` if the index is
|
||||
/// valid; otherwise, `nil`.
|
||||
public func elementAt(_ index: Index) -> Element? {
|
||||
return indices.contains(index) ? self[index] : nil
|
||||
}
|
||||
|
||||
/// Checks whether the given key-value pair can be inserted into to ordered dictionary by
|
||||
/// validating the presence of the key.
|
||||
///
|
||||
/// - Parameter newElement: The key-value pair to be inserted into the ordered dictionary.
|
||||
/// - Returns: `true` if the key-value pair can be safely inserted; otherwise, `false`.
|
||||
///
|
||||
/// - SeeAlso: canInsert(key:)
|
||||
/// - SeeAlso: canInsert(at:)
|
||||
@available(*, deprecated, message: "Use canInsert(key:) with the element's key instead")
|
||||
public func canInsert(_ newElement: Element) -> Bool {
|
||||
return canInsert(key: newElement.key)
|
||||
}
|
||||
|
||||
/// Checks whether a key-value pair with the given key can be inserted into the ordered
|
||||
/// dictionary by validating its presence.
|
||||
///
|
||||
/// - Parameter key: The key to be inserted into the ordered dictionary.
|
||||
/// - Returns: `true` if the key can safely be inserted; ortherwise, `false`.
|
||||
///
|
||||
/// - SeeAlso: canInsert(at:)
|
||||
public func canInsert(key: Key) -> Bool {
|
||||
return !containsKey(key)
|
||||
}
|
||||
|
||||
/// Checks whether a new key-value pair can be inserted into the ordered dictionary at the
|
||||
/// given index.
|
||||
///
|
||||
/// - Parameter index: The index the new key-value pair should be inserted at.
|
||||
/// - Returns: `true` if a new key-value pair can be inserted at the specified index; otherwise,
|
||||
/// `false`.
|
||||
///
|
||||
/// - SeeAlso: canInsert(key:)
|
||||
public func canInsert(at index: Index) -> Bool {
|
||||
return index >= startIndex && index <= endIndex
|
||||
}
|
||||
|
||||
/// Inserts a new key-value pair at the specified position.
|
||||
///
|
||||
/// If the key of the inserted pair already exists in the ordered dictionary, a runtime error
|
||||
/// is triggered. Use `canInsert(_:)` for performing a check first, so that this method can
|
||||
/// be executed safely.
|
||||
///
|
||||
/// - Parameter newElement: The new key-value pair to insert into the ordered dictionary. The
|
||||
/// key contained in the pair must not be already present in the ordered dictionary.
|
||||
/// - Parameter index: The position at which to insert the new key-value pair. `index` must be
|
||||
/// a valid index of the ordered dictionary or equal to `endIndex` property.
|
||||
///
|
||||
/// - SeeAlso: canInsert(key:)
|
||||
/// - SeeAlso: canInsert(at:)
|
||||
/// - SeeAlso: update(:at:)
|
||||
public mutating func insert(_ newElement: Element, at index: Index) {
|
||||
precondition(canInsert(key: newElement.key), "Cannot insert duplicate key in OrderedDictionary")
|
||||
precondition(canInsert(at: index), "Cannot insert at invalid index in OrderedDictionary")
|
||||
|
||||
let (key, value) = newElement
|
||||
|
||||
_orderedKeys.insert(key, at: index)
|
||||
_keysToValues[key] = value
|
||||
}
|
||||
|
||||
/// Checks whether the key-value pair at the given index can be updated with the given key-value
|
||||
/// pair. This is not the case if the key of the updated element is already present in the
|
||||
/// ordered dictionary and located at another index than the updated one.
|
||||
///
|
||||
/// Although this is a checking method, a valid index has to be provided.
|
||||
///
|
||||
/// - Parameter newElement: The key-value pair to be set at the specified position.
|
||||
/// - Parameter index: The position at which to set the key-value pair. `index` must be a valid
|
||||
/// index of the ordered dictionary.
|
||||
public func canUpdate(_ newElement: Element, at index: Index) -> Bool {
|
||||
var keyPresentAtIndex = false
|
||||
return _canUpdate(newElement, at: index, keyPresentAtIndex: &keyPresentAtIndex)
|
||||
}
|
||||
|
||||
/// Updates the key-value pair located at the specified position.
|
||||
///
|
||||
/// If the key of the updated pair already exists in the ordered dictionary *and* is located at
|
||||
/// a different position than the specified one, a runtime error is triggered. Use
|
||||
/// `canUpdate(_:at:)` for performing a check first, so that this method can be executed safely.
|
||||
///
|
||||
/// - Parameter newElement: The key-value pair to be set at the specified position.
|
||||
/// - Parameter index: The position at which to set the key-value pair. `index` must be a valid
|
||||
/// index of the ordered dictionary.
|
||||
///
|
||||
/// - SeeAlso: canUpdate(_:at:)
|
||||
/// - SeeAlso: insert(:at:)
|
||||
@discardableResult
|
||||
public mutating func update(_ newElement: Element, at index: Index) -> Element? {
|
||||
// Store the flag indicating whether the key of the inserted element
|
||||
// is present at the updated index
|
||||
var keyPresentAtIndex = false
|
||||
|
||||
precondition(
|
||||
_canUpdate(newElement, at: index, keyPresentAtIndex: &keyPresentAtIndex),
|
||||
"OrderedDictionary update duplicates key"
|
||||
)
|
||||
|
||||
// Decompose the element
|
||||
let (key, value) = newElement
|
||||
|
||||
// Load the current element at the index
|
||||
let replacedElement = self[index]
|
||||
|
||||
// If its key differs, remove its associated value
|
||||
if (!keyPresentAtIndex) {
|
||||
_keysToValues.removeValue(forKey: replacedElement.key)
|
||||
}
|
||||
|
||||
// Store the new position of the key and the new value associated with the key
|
||||
_orderedKeys[index] = key
|
||||
_keysToValues[key] = value
|
||||
|
||||
return replacedElement
|
||||
}
|
||||
|
||||
/// Removes and returns the key-value pair at the specified position if there is any key-value
|
||||
/// pair, or `nil` if there is none.
|
||||
///
|
||||
/// - Parameter index: The position of the key-value pair to remove.
|
||||
/// - Returns: The element at the specified index, or `nil` if the position is not taken.
|
||||
///
|
||||
/// - SeeAlso: removeValue(forKey:)
|
||||
@discardableResult
|
||||
public mutating func remove(at index: Index) -> Element? {
|
||||
guard let element = elementAt(index) else { return nil }
|
||||
|
||||
_orderedKeys.remove(at: index)
|
||||
_keysToValues.removeValue(forKey: element.key)
|
||||
|
||||
return element
|
||||
}
|
||||
|
||||
private func _canUpdate(
|
||||
_ newElement: Element,
|
||||
at index: Index,
|
||||
keyPresentAtIndex: inout Bool
|
||||
) -> Bool {
|
||||
precondition(indices.contains(index), "OrderedDictionary index is out of range")
|
||||
|
||||
let currentIndexOfKey = self.index(forKey: newElement.key)
|
||||
|
||||
let keyNotPresent = currentIndexOfKey == nil
|
||||
keyPresentAtIndex = currentIndexOfKey == index
|
||||
|
||||
return keyNotPresent || keyPresentAtIndex
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Removing First & Last Elements
|
||||
// ======================================================= //
|
||||
|
||||
/// Removes and returns the first key-value pair of the ordered dictionary if it is not empty.
|
||||
public mutating func popFirst() -> Element? {
|
||||
guard !isEmpty else { return nil }
|
||||
return remove(at: startIndex)
|
||||
}
|
||||
|
||||
/// Removes and returns the last key-value pair of the ordered dictionary if it is not empty.
|
||||
public mutating func popLast() -> Element? {
|
||||
guard !isEmpty else { return nil }
|
||||
return remove(at: index(before: endIndex))
|
||||
}
|
||||
|
||||
/// Removes and returns the first key-value pair of the ordered dictionary.
|
||||
public mutating func removeFirst() -> Element {
|
||||
precondition(!isEmpty, "Cannot remove key-value pairs from empty OrderedDictionary")
|
||||
return remove(at: startIndex)!
|
||||
}
|
||||
|
||||
/// Removes and returns the last key-value pair of the ordered dictionary.
|
||||
public mutating func removeLast() -> Element {
|
||||
precondition(!isEmpty, "Cannot remove key-value pairs from empty OrderedDictionary")
|
||||
return remove(at: index(before: endIndex))!
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Moving Elements
|
||||
// ======================================================= //
|
||||
|
||||
/// Moves an existing key-value pair specified by the given key to the new index by removing it
|
||||
/// from its original index first and inserting it at the new index. If the movement is
|
||||
/// actually performed, the previous index of the key-value pair is returned. Otherwise, `nil`
|
||||
/// is returned.
|
||||
///
|
||||
/// - Parameter key: The key specifying the key-value pair to move.
|
||||
/// - Parameter newIndex: The new index the key-value pair should be moved to.
|
||||
/// - Returns: The previous index of the key-value pair if it was sucessfully moved.
|
||||
@discardableResult
|
||||
public mutating func moveElement(forKey key: Key, to newIndex: Index) -> Index? {
|
||||
// Load the previous index and return nil if the index is not found.
|
||||
guard let previousIndex = index(forKey: key) else { return nil }
|
||||
|
||||
// If the previous and new indices match, threat it as if the movement was already
|
||||
// performed.
|
||||
guard previousIndex != newIndex else { return previousIndex }
|
||||
|
||||
// Remove the value for the key at its original index.
|
||||
let value = removeValue(forKey: key)!
|
||||
|
||||
// Validate the new index.
|
||||
precondition(canInsert(at: newIndex), "Cannot move to invalid index in OrderedDictionary")
|
||||
|
||||
// Insert the element at the new index.
|
||||
insert((key: key, value: value), at: newIndex)
|
||||
|
||||
return previousIndex
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Sorting Elements
|
||||
// ======================================================= //
|
||||
|
||||
/// Sorts the ordered dictionary in place, using the given predicate as the comparison between
|
||||
/// elements.
|
||||
///
|
||||
/// The predicate must be a *strict weak ordering* over the elements.
|
||||
///
|
||||
/// - Parameter areInIncreasingOrder: A predicate that returns `true` if its first argument
|
||||
/// should be ordered before its second argument; otherwise, `false`.
|
||||
///
|
||||
/// - SeeAlso: MutableCollection.sort(by:), sorted(by:)
|
||||
public mutating func sort(
|
||||
by areInIncreasingOrder: (Element, Element) throws -> Bool
|
||||
) rethrows {
|
||||
_orderedKeys = try _sortedElements(by: areInIncreasingOrder).map { $0.key }
|
||||
}
|
||||
|
||||
/// Returns a new ordered dictionary, sorted using the given predicate as the comparison between
|
||||
/// elements.
|
||||
///
|
||||
/// The predicate must be a *strict weak ordering* over the elements.
|
||||
///
|
||||
/// - Parameter areInIncreasingOrder: A predicate that returns `true` if its first argument
|
||||
/// should be ordered before its second argument; otherwise, `false`.
|
||||
/// - Returns: A new ordered dictionary sorted according to the predicate.
|
||||
///
|
||||
/// - SeeAlso: MutableCollection.sorted(by:), sort(by:)
|
||||
/// - MutatingVariant: sort
|
||||
public func sorted(
|
||||
by areInIncreasingOrder: (Element, Element) throws -> Bool
|
||||
) rethrows -> OrderedDictionary<Key, Value> {
|
||||
return OrderedDictionary(uniqueKeysWithValues: try _sortedElements(by: areInIncreasingOrder))
|
||||
}
|
||||
|
||||
private func _sortedElements(
|
||||
by areInIncreasingOrder: (Element, Element) throws -> Bool
|
||||
) rethrows -> [Element] {
|
||||
return try sorted(by: areInIncreasingOrder)
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Mapping Values
|
||||
// ======================================================= //
|
||||
|
||||
/// Returns a new ordered dictionary containing the keys of this ordered dictionary with the
|
||||
/// values transformed by the given closure by preserving the original order.
|
||||
public func mapValues<T>(
|
||||
_ transform: (Value) throws -> T
|
||||
) rethrows -> OrderedDictionary<Key, T> {
|
||||
var result = OrderedDictionary<Key, T>()
|
||||
|
||||
for (key, value) in self {
|
||||
result[key] = try transform(value)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/// Returns a new ordered dictionary containing only the key-value pairs that have non-nil
|
||||
/// values as the result of transformation by the given closure by preserving the original
|
||||
/// order.
|
||||
public func compactMapValues<T>(
|
||||
_ transform: (Value) throws -> T?
|
||||
) rethrows -> OrderedDictionary<Key, T> {
|
||||
var result = OrderedDictionary<Key, T>()
|
||||
|
||||
for (key, value) in self {
|
||||
if let transformedValue = try transform(value) {
|
||||
result[key] = transformedValue
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Capacity
|
||||
// ======================================================= //
|
||||
|
||||
/// The total number of elements that the ordered dictionary can contain without allocating
|
||||
/// new storage.
|
||||
public var capacity: Int {
|
||||
return Swift.min(_orderedKeys.capacity, _keysToValues.capacity)
|
||||
}
|
||||
|
||||
/// Reserves enough space to store the specified number of elements, when appropriate
|
||||
/// for the underlying types.
|
||||
///
|
||||
/// If you are adding a known number of elements to an ordered dictionary, use this method
|
||||
/// to avoid multiple reallocations. This method ensures that the underlying types of the
|
||||
/// ordered dictionary have space allocated for at least the requested number of elements.
|
||||
///
|
||||
/// - Parameter minimumCapacity: The requested number of elements to store.
|
||||
public mutating func reserveCapacity(_ minimumCapacity: Int) {
|
||||
_orderedKeys.reserveCapacity(minimumCapacity)
|
||||
_keysToValues.reserveCapacity(minimumCapacity)
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Internal Storage
|
||||
// ======================================================= //
|
||||
|
||||
/// The backing storage for the ordered keys.
|
||||
fileprivate var _orderedKeys: [Key]
|
||||
|
||||
/// The backing storage for the mapping of keys to values.
|
||||
fileprivate var _keysToValues: [Key: Value]
|
||||
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Subtypes
|
||||
// ======================================================= //
|
||||
|
||||
/// A view into an ordered dictionary whose indices are a subrange of the indices of the ordered
|
||||
/// dictionary.
|
||||
public typealias OrderedDictionarySlice<Key: Hashable, Value> = Slice<OrderedDictionary<Key, Value>>
|
||||
|
||||
/// A collection containing the keys of the ordered dictionary.
|
||||
///
|
||||
/// Under the hood this is a lazily evaluated bidirectional collection deriving the keys from
|
||||
/// the base ordered dictionary on-the-fly.
|
||||
public typealias OrderedDictionaryKeys<Key: Hashable, Value> = LazyMapCollection<OrderedDictionary<Key, Value>, Key>
|
||||
|
||||
/// A collection containing the values of the ordered dictionary.
|
||||
///
|
||||
/// Under the hood this is a lazily evaluated bidirectional collection deriving the values from
|
||||
/// the base ordered dictionary on-the-fly.
|
||||
public typealias OrderedDictionaryValues<Key: Hashable, Value> = LazyMapCollection<OrderedDictionary<Key, Value>, Value>
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Literals
|
||||
// ======================================================= //
|
||||
|
||||
extension OrderedDictionary: ExpressibleByArrayLiteral {
|
||||
|
||||
/// Initializes an ordered dictionary initialized from an array literal containing a list of
|
||||
/// key-value pairs. Every key in `elements` must be unique.
|
||||
public init(arrayLiteral elements: Element...) {
|
||||
self.init(uniqueKeysWithValues: elements)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension OrderedDictionary: ExpressibleByDictionaryLiteral {
|
||||
|
||||
/// Initializes an ordered dictionary initialized from a dictionary literal. Every key in
|
||||
/// `elements` must be unique.
|
||||
public init(dictionaryLiteral elements: (Key, Value)...) {
|
||||
self.init(uniqueKeysWithValues: elements.map { element in
|
||||
let (key, value) = element
|
||||
return (key: key, value: value)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Equatable Conformance
|
||||
// ======================================================= //
|
||||
|
||||
extension OrderedDictionary: Equatable where Value: Equatable {}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Dictionary Extension
|
||||
// ======================================================= //
|
||||
|
||||
extension Dictionary {
|
||||
|
||||
/// Returns an ordered dictionary containing the key-value pairs from the dictionary, sorted
|
||||
/// using the given sort function.
|
||||
///
|
||||
/// - Parameter areInIncreasingOrder: The sort function which compares the key-value pairs.
|
||||
/// - Returns: The ordered dictionary.
|
||||
/// - SeeAlso: OrderedDictionary.init(unsorted:areInIncreasingOrder:)
|
||||
public func sorted(
|
||||
by areInIncreasingOrder: (Element, Element) throws -> Bool
|
||||
) rethrows -> OrderedDictionary<Key, Value> {
|
||||
return try OrderedDictionary(
|
||||
unsorted: self,
|
||||
areInIncreasingOrder: areInIncreasingOrder
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
extension Dictionary {
|
||||
|
||||
/// Returns an ordered dictionary containing the key-value pairs from the dictionary, sorted
|
||||
/// using the given sort function.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - areInIncreasingOrder: The sort function which compares the key-value pairs.
|
||||
/// - Returns: The ordered dictionary.
|
||||
///
|
||||
/// - SeeAlso: `OrderedDictionary.init(unsorted:areInIncreasingOrder:)`
|
||||
public func sorted(
|
||||
by areInIncreasingOrder: (Element, Element) throws -> Bool
|
||||
) rethrows -> OrderedDictionary<Key, Value> {
|
||||
return try OrderedDictionary(
|
||||
unsorted: self,
|
||||
areInIncreasingOrder: areInIncreasingOrder
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
extension OrderedDictionary: Encodable where Key: Encodable, Value: Encodable {
|
||||
|
||||
/// __inheritdoc__
|
||||
/// Encodes the contents of this ordered dictionary into the given encoder.
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
// Encode the ordered dictionary as an array of alternating key-value pairs.
|
||||
var container = encoder.unkeyedContainer()
|
||||
@@ -15,7 +15,7 @@ extension OrderedDictionary: Encodable where Key: Encodable, Value: Encodable {
|
||||
|
||||
extension OrderedDictionary: Decodable where Key: Decodable, Value: Decodable {
|
||||
|
||||
/// __inheritdoc__
|
||||
/// Creates a new ordered dictionary by decoding from the given decoder.
|
||||
public init(from decoder: Decoder) throws {
|
||||
// Decode the ordered dictionary from an array of alternating key-value pairs.
|
||||
self.init()
|
||||
@@ -0,0 +1,81 @@
|
||||
extension OrderedDictionary {
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Initialization
|
||||
// ============================================================================ //
|
||||
|
||||
@available(*, deprecated, message: "Please use init(values:uniquelyKeyedBy:).", renamed: "init(values:uniquelyKeyedBy:)")
|
||||
public init<S: Sequence>(
|
||||
values: S,
|
||||
keyedBy extractKey: (Value) -> Key
|
||||
) where S.Element == Value {
|
||||
self.init(values: values, uniquelyKeyedBy: extractKey)
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "Please use init(values:uniquelyKeyedBy:).", renamed: "init(values:uniquelyKeyedBy:)")
|
||||
public init(
|
||||
values: [Value],
|
||||
keyedBy keyPath: KeyPath<Value, Key>
|
||||
) {
|
||||
self.init(values: values, uniquelyKeyedBy: keyPath)
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "Please use init(uniqueKeysWithValues:).", renamed: "init(uniqueKeysWithValues:)")
|
||||
public init<S: Sequence>(_ elements: S) where S.Element == Element {
|
||||
self.init(uniqueKeysWithValues: elements)
|
||||
}
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Insertion Checks
|
||||
// ============================================================================ //
|
||||
|
||||
/// Checks whether the given key-value pair can be inserted into to ordered dictionary
|
||||
/// by validating the presence of the key.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - newElement: The key-value pair to be inserted into the ordered dictionary.
|
||||
/// - Returns: `true` if the key-value pair can be safely inserted; otherwise, `false`.
|
||||
///
|
||||
/// - SeeAlso: `canInsert(key:)`
|
||||
/// - SeeAlso: `canInsert(at:)`
|
||||
@available(*, deprecated, message: "Use canInsert(key:) with the element's key instead.")
|
||||
public func canInsert(_ newElement: Element) -> Bool {
|
||||
return canInsert(key: newElement.key)
|
||||
}
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Moving Elements
|
||||
// ============================================================================ //
|
||||
|
||||
/// Moves an existing key-value pair specified by the given key to the new index by removing
|
||||
/// it from its original index first and inserting it at the new index. If the movement is
|
||||
/// actually performed, the previous index of the key-value pair is returned. Otherwise, `nil`
|
||||
/// is returned.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - key: The key specifying the key-value pair to move.
|
||||
/// - newIndex: The new index the key-value pair should be moved to.
|
||||
/// - Returns: The previous index of the key-value pair if it was sucessfully moved.
|
||||
@available(*, deprecated, message: "Since the concrete behavior of the element movement highly depends on concrete use cases, its official support will be dropped in the future. Please use the public API for modeling a move operation instead.")
|
||||
@discardableResult
|
||||
public mutating func moveElement(forKey key: Key, to newIndex: Index) -> Index? {
|
||||
// Load the previous index and return nil if the index is not found.
|
||||
guard let previousIndex = index(forKey: key) else { return nil }
|
||||
|
||||
// If the previous and new indices match, treat it as if the movement was already
|
||||
// performed.
|
||||
guard previousIndex != newIndex else { return previousIndex }
|
||||
|
||||
// Remove the value for the key at its original index.
|
||||
let value = removeValue(forKey: key)!
|
||||
|
||||
// Validate the new index.
|
||||
precondition(canInsert(at: newIndex), "Cannot move to invalid index in OrderedDictionary")
|
||||
|
||||
// Insert the element at the new index.
|
||||
insert((key: key, value: value), at: newIndex)
|
||||
|
||||
return previousIndex
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,105 @@
|
||||
public struct OrderedDictionarySlice<Key: Hashable, Value>: RandomAccessCollection, MutableCollection {
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Type Aliases
|
||||
// ============================================================================ //
|
||||
|
||||
/// The type of the underlying ordered dictionary.
|
||||
public typealias Base = OrderedDictionary<Key, Value>
|
||||
|
||||
/// The type of the contiguous subrange of the ordered dictionary's elements.
|
||||
public typealias SubSequence = Self
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Initialization
|
||||
// ============================================================================ //
|
||||
|
||||
public init(base: Base, bounds: Base.Indices) {
|
||||
self.base = base
|
||||
self.startIndex = bounds.lowerBound
|
||||
self.endIndex = bounds.upperBound
|
||||
}
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Base
|
||||
// ============================================================================ //
|
||||
|
||||
/// The underlying ordered dictionary.
|
||||
public private(set) var base: Base
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Indices
|
||||
// ============================================================================ //
|
||||
|
||||
/// The start index.
|
||||
public let startIndex: Base.Index
|
||||
|
||||
/// The end index.
|
||||
public let endIndex: Base.Index
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Subscripts
|
||||
// ============================================================================ //
|
||||
|
||||
public subscript(
|
||||
position: Base.Index
|
||||
) -> Base.Element {
|
||||
get {
|
||||
base[position]
|
||||
}
|
||||
set(newElement) {
|
||||
base[position] = newElement
|
||||
}
|
||||
}
|
||||
|
||||
public subscript(
|
||||
bounds: Range<Int>
|
||||
) -> OrderedDictionarySlice<Key, Value> {
|
||||
get {
|
||||
base[bounds]
|
||||
}
|
||||
set(newElements) {
|
||||
base[bounds] = newElements
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Reordering Methods Overloads
|
||||
// ============================================================================ //
|
||||
|
||||
public mutating func sort(
|
||||
by areInIncreasingOrder: (Base.Element, Base.Element) throws -> Bool
|
||||
) rethrows {
|
||||
try base._sort(
|
||||
in: indices,
|
||||
by: areInIncreasingOrder
|
||||
)
|
||||
}
|
||||
|
||||
public mutating func reverse() {
|
||||
base._reverse(in: indices)
|
||||
}
|
||||
|
||||
public mutating func shuffle<T>(
|
||||
using generator: inout T
|
||||
) where T: RandomNumberGenerator {
|
||||
base._shuffle(
|
||||
in: indices,
|
||||
using: &generator
|
||||
)
|
||||
}
|
||||
|
||||
public mutating func partition(
|
||||
by belongsInSecondPartition: (Base.Element) throws -> Bool
|
||||
) rethrows -> Index {
|
||||
return try base._partition(
|
||||
in: indices,
|
||||
by: belongsInSecondPartition
|
||||
)
|
||||
}
|
||||
|
||||
public mutating func swapAt(_ i: Base.Index, _ j: Base.Index) {
|
||||
base.swapAt(i, j)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2015-2020 Lukas Kubanek. All rights reserved.</string>
|
||||
<string>Copyright © 2015-2021 Lukas Kubanek. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
|
||||
@@ -1,672 +0,0 @@
|
||||
import OrderedDictionary
|
||||
import Foundation
|
||||
import XCTest
|
||||
|
||||
struct TestValue: Equatable {
|
||||
var string: String
|
||||
static func == (lhs: TestValue, rhs: TestValue) -> Bool {
|
||||
return lhs.string == rhs.string
|
||||
}
|
||||
}
|
||||
|
||||
class OrderedDictionaryTests: XCTestCase {
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Initialization
|
||||
// ======================================================= //
|
||||
|
||||
func testInitializationUsingArrayLiteral() {
|
||||
let actual: OrderedDictionary<String, Int> = [
|
||||
("A", 1),
|
||||
("B", 2),
|
||||
("C", 3)
|
||||
]
|
||||
|
||||
let expected = OrderedDictionary<String, Int>(uniqueKeysWithValues: [
|
||||
(key: "A", value: 1),
|
||||
(key: "B", value: 2),
|
||||
(key: "C", value: 3)
|
||||
])
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testInitializationUsingDictionaryLiteral() {
|
||||
let actual: OrderedDictionary<String, Int> = [
|
||||
"A": 1,
|
||||
"B": 2,
|
||||
"C": 3
|
||||
]
|
||||
|
||||
let expected = OrderedDictionary<String, Int>(uniqueKeysWithValues: [
|
||||
(key: "A", value: 1),
|
||||
(key: "B", value: 2),
|
||||
(key: "C", value: 3)
|
||||
])
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testInitializationUsingValuesAndKeyProviderClosure() {
|
||||
let actual = OrderedDictionary(
|
||||
values: [1, 2, 3],
|
||||
uniquelyKeyedBy: { "\($0)" }
|
||||
)
|
||||
|
||||
let expected = OrderedDictionary<String, Int>(uniqueKeysWithValues: [
|
||||
(key: "1", value: 1),
|
||||
(key: "2", value: 2),
|
||||
(key: "3", value: 3)
|
||||
])
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testInitializationUsingValuesAnyKeyPath() {
|
||||
let actual = OrderedDictionary(
|
||||
values: [
|
||||
TestValue(string: "A"),
|
||||
TestValue(string: "B"),
|
||||
TestValue(string: "C")
|
||||
],
|
||||
uniquelyKeyedBy: \.string
|
||||
)
|
||||
|
||||
let expected = OrderedDictionary<String, TestValue>(uniqueKeysWithValues: [
|
||||
(key: "A", value: TestValue(string: "A")),
|
||||
(key: "B", value: TestValue(string: "B")),
|
||||
(key: "C", value: TestValue(string: "C"))
|
||||
])
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testInitializationUsingUnsortedDictionaryAndSortFunction() {
|
||||
let actual = OrderedDictionary(
|
||||
unsorted: [
|
||||
2: "foo",
|
||||
1: "bar",
|
||||
4: "baz",
|
||||
5: "bat",
|
||||
3: "bam"
|
||||
],
|
||||
areInIncreasingOrder: { $0.key < $1.key }
|
||||
)
|
||||
|
||||
let expected = OrderedDictionary(uniqueKeysWithValues: [
|
||||
(key: 1, value: "bar"),
|
||||
(key: 2, value: "foo"),
|
||||
(key: 3, value: "bam"),
|
||||
(key: 4, value: "baz"),
|
||||
(key: 5, value: "bat")
|
||||
])
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testCreationFromDictionary() {
|
||||
let actual = [
|
||||
2: "foo",
|
||||
1: "bar",
|
||||
4: "baz",
|
||||
5: "bat",
|
||||
3: "bam"
|
||||
].sorted(by: { $0.key < $1.key })
|
||||
|
||||
let expected = OrderedDictionary(uniqueKeysWithValues: [
|
||||
(key: 1, value: "bar"),
|
||||
(key: 2, value: "foo"),
|
||||
(key: 3, value: "bam"),
|
||||
(key: 4, value: "baz"),
|
||||
(key: 5, value: "bat")
|
||||
])
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Content Access
|
||||
// ======================================================= //
|
||||
|
||||
func testAccessingContent() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3]
|
||||
|
||||
XCTAssertEqual(orderedDictionary.count, 3)
|
||||
|
||||
XCTAssertEqual(orderedDictionary["A"], 1)
|
||||
XCTAssertEqual(orderedDictionary.value(forKey: "A"), 1)
|
||||
XCTAssertEqual(orderedDictionary.index(forKey: "A"), 0)
|
||||
XCTAssertTrue(orderedDictionary.containsKey("A"))
|
||||
XCTAssertTrue(orderedDictionary[0] == ("A", 1))
|
||||
|
||||
XCTAssertEqual(orderedDictionary["B"], 2)
|
||||
XCTAssertEqual(orderedDictionary.value(forKey: "B"), 2)
|
||||
XCTAssertEqual(orderedDictionary.index(forKey: "B"), 1)
|
||||
XCTAssertTrue(orderedDictionary.containsKey("B"))
|
||||
XCTAssertTrue(orderedDictionary[1] == ("B", 2))
|
||||
|
||||
XCTAssertEqual(orderedDictionary["C"], 3)
|
||||
XCTAssertEqual(orderedDictionary.value(forKey: "C"), 3)
|
||||
XCTAssertEqual(orderedDictionary.index(forKey: "C"), 2)
|
||||
XCTAssertTrue(orderedDictionary.containsKey("C"))
|
||||
XCTAssertTrue(orderedDictionary[2] == ("C", 3))
|
||||
}
|
||||
|
||||
func testCreatingIterator() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3]
|
||||
var iterator = orderedDictionary.makeIterator()
|
||||
|
||||
let indexes = [0, 1, 2]
|
||||
var indexesIterator = indexes.makeIterator()
|
||||
|
||||
while let (actualKey, actualValue) = iterator.next() {
|
||||
let index = indexesIterator.next()
|
||||
let (expectedKey, expectedValue) = orderedDictionary[index!]
|
||||
|
||||
XCTAssertEqual(actualKey, expectedKey)
|
||||
XCTAssertEqual(actualValue, expectedValue)
|
||||
}
|
||||
|
||||
XCTAssertNil(iterator.next())
|
||||
XCTAssertNil(indexesIterator.next())
|
||||
}
|
||||
|
||||
func testAccessingOrderedKeys() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3]
|
||||
let actual = Array(orderedDictionary.orderedKeys)
|
||||
|
||||
let expected = ["A", "B", "C"]
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testAccessingOrderedValues() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3]
|
||||
let actual = Array(orderedDictionary.orderedValues)
|
||||
|
||||
let expected = [1, 2, 3]
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testAccessingUnsortedDictionary() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3]
|
||||
let actual = orderedDictionary.unorderedDictionary
|
||||
|
||||
let expected = ["A": 1, "B": 2, "C": 3]
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Key-based Modifications
|
||||
// ======================================================= //
|
||||
|
||||
func testKeyBasedModifications() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3]
|
||||
|
||||
orderedDictionary["A"] = 5
|
||||
orderedDictionary["D"] = 10
|
||||
orderedDictionary["B"] = nil
|
||||
|
||||
XCTAssertEqual(orderedDictionary.count, 3)
|
||||
|
||||
XCTAssertEqual(orderedDictionary["A"], 5)
|
||||
XCTAssertEqual(orderedDictionary.index(forKey: "A"), 0)
|
||||
XCTAssertTrue(orderedDictionary.containsKey("A"))
|
||||
|
||||
XCTAssertNil(orderedDictionary["B"])
|
||||
XCTAssertNil(orderedDictionary.index(forKey: "B"))
|
||||
XCTAssertFalse(orderedDictionary.containsKey("B"))
|
||||
|
||||
XCTAssertEqual(orderedDictionary["C"], 3)
|
||||
XCTAssertEqual(orderedDictionary.index(forKey: "C"), 1)
|
||||
XCTAssertTrue(orderedDictionary.containsKey("C"))
|
||||
|
||||
XCTAssertEqual(orderedDictionary["D"], 10)
|
||||
XCTAssertEqual(orderedDictionary.index(forKey: "D"), 2)
|
||||
XCTAssertTrue(orderedDictionary.containsKey("D"))
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Index-based Insertions
|
||||
// ======================================================= //
|
||||
|
||||
func testIndexBasedInsertionsWithUniqueKeys() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3]
|
||||
orderedDictionary.insert((key: "T", value: 15), at: 0)
|
||||
orderedDictionary.insert((key: "U", value: 16), at: 2)
|
||||
orderedDictionary.insert((key: "V", value: 17), at: 5)
|
||||
orderedDictionary.insert((key: "W", value: 18), at: 2)
|
||||
let actual = orderedDictionary
|
||||
|
||||
let expected: OrderedDictionary<String, Int> = ["T": 15, "A": 1, "W": 18, "U": 16, "B": 2, "C": 3, "V": 17]
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testIndexBasedInsertionWithDuplicateKey() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3]
|
||||
let invalidKey = "A"
|
||||
|
||||
XCTAssertFalse(orderedDictionary.canInsert(key: invalidKey))
|
||||
}
|
||||
|
||||
func testIndexBasedInsertionWithNegativeIndex() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3]
|
||||
let invalidIndex = -1
|
||||
|
||||
XCTAssertFalse(orderedDictionary.canInsert(at: invalidIndex))
|
||||
}
|
||||
|
||||
func testIndexBasedInsertionWithIndexOutOfBounds() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3]
|
||||
let invalidIndex = 4
|
||||
|
||||
XCTAssertFalse(orderedDictionary.canInsert(at: invalidIndex))
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Index-based Updates
|
||||
// ======================================================= //
|
||||
|
||||
func testIndexBasedUpdateMethodWithNewUniqueKey() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3]
|
||||
let previousElement = orderedDictionary.update((key: "D", value: 4), at: 1)
|
||||
|
||||
XCTAssertEqual(orderedDictionary.count, 3)
|
||||
XCTAssertTrue(previousElement! == ("B", 2))
|
||||
|
||||
let actual = orderedDictionary
|
||||
let expected: OrderedDictionary<String, Int> = ["A": 1, "D": 4, "C": 3]
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testIndexBasedUpdateMethodByReplacingSameKey() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3]
|
||||
let previousElement = orderedDictionary.update((key: "B", value: 42), at: 1)
|
||||
|
||||
XCTAssertEqual(orderedDictionary.count, 3)
|
||||
XCTAssertTrue(previousElement! == ("B", 2))
|
||||
|
||||
let actual = orderedDictionary
|
||||
let expected: OrderedDictionary<String, Int> = ["A": 1, "B": 42, "C": 3]
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testIndexBasedUpdateMethodByDuplicatingKey() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3]
|
||||
|
||||
let element = (key: "A", value: 42)
|
||||
|
||||
XCTAssertTrue(orderedDictionary.canUpdate(element, at: 0))
|
||||
XCTAssertFalse(orderedDictionary.canUpdate(element, at: 1))
|
||||
XCTAssertFalse(orderedDictionary.canUpdate(element, at: 2))
|
||||
}
|
||||
|
||||
func testRetrievingElementAtNonExistentIndex() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3]
|
||||
XCTAssertNil(orderedDictionary.elementAt(42))
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Content Removal
|
||||
// ======================================================= //
|
||||
|
||||
func testRemoveAll() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3]
|
||||
|
||||
orderedDictionary.removeAll()
|
||||
|
||||
XCTAssertEqual(orderedDictionary.count, 0)
|
||||
}
|
||||
|
||||
func testKeyBasedRemoval() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3]
|
||||
|
||||
let removedValue1 = orderedDictionary.removeValue(forKey: "A")
|
||||
let removedValue2 = orderedDictionary.removeValue(forKey: "K")
|
||||
|
||||
XCTAssertEqual(removedValue1, 1)
|
||||
XCTAssertNil(removedValue2)
|
||||
|
||||
XCTAssertEqual(orderedDictionary.count, 2)
|
||||
|
||||
XCTAssertNil(orderedDictionary["A"])
|
||||
XCTAssertNil(orderedDictionary.index(forKey: "A"))
|
||||
|
||||
XCTAssertEqual(orderedDictionary["B"], 2)
|
||||
XCTAssertEqual(orderedDictionary.index(forKey: "B"), 0)
|
||||
|
||||
XCTAssertEqual(orderedDictionary["C"], 3)
|
||||
XCTAssertEqual(orderedDictionary.index(forKey: "C"), 1)
|
||||
}
|
||||
|
||||
func testIndexBasedRemoval() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3, "D": 4]
|
||||
|
||||
let (actualKey1, actualValue1) = orderedDictionary.remove(at: 0)!
|
||||
let (expectedKey1, expectedValue1) = ("A", 1)
|
||||
|
||||
XCTAssertEqual(actualKey1, expectedKey1)
|
||||
XCTAssertEqual(actualValue1, expectedValue1)
|
||||
|
||||
let (actualKey2, actualValue2) = orderedDictionary.remove(at: 2)!
|
||||
let (expectedKey2, expectedValue2) = ("D", 4)
|
||||
|
||||
XCTAssertEqual(actualKey2, expectedKey2)
|
||||
XCTAssertEqual(actualValue2, expectedValue2)
|
||||
|
||||
let nonExistentElement = orderedDictionary.remove(at: 42)
|
||||
|
||||
XCTAssertNil(nonExistentElement)
|
||||
|
||||
let actual = orderedDictionary
|
||||
let expected: OrderedDictionary<String, Int> = ["B": 2, "C": 3]
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testPopFirstEmpty() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = []
|
||||
let first = orderedDictionary.popFirst()
|
||||
|
||||
XCTAssertNil(first)
|
||||
}
|
||||
|
||||
func testPopFirstNonEmpty() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3, "D": 4]
|
||||
|
||||
let (actualKey, actualValue) = orderedDictionary.popFirst()!
|
||||
let (expectedKey, expectedValue) = ("A", 1)
|
||||
|
||||
XCTAssertEqual(actualKey, expectedKey)
|
||||
XCTAssertEqual(actualValue, expectedValue)
|
||||
|
||||
let actual = orderedDictionary
|
||||
let expected: OrderedDictionary<String, Int> = ["B": 2, "C": 3, "D": 4]
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testPopLastEmpty() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = []
|
||||
let last = orderedDictionary.popLast()
|
||||
|
||||
XCTAssertNil(last)
|
||||
}
|
||||
|
||||
func testPopLastNonEmpty() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3, "D": 4]
|
||||
|
||||
let (actualKey, actualValue) = orderedDictionary.popLast()!
|
||||
let (expectedKey, expectedValue) = ("D", 4)
|
||||
|
||||
XCTAssertEqual(actualKey, expectedKey)
|
||||
XCTAssertEqual(actualValue, expectedValue)
|
||||
|
||||
let actual = orderedDictionary
|
||||
let expected: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3]
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testRemoveFirstNonEmpty() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3, "D": 4]
|
||||
|
||||
let (actualKey, actualValue) = orderedDictionary.removeFirst()
|
||||
let (expectedKey, expectedValue) = ("A", 1)
|
||||
|
||||
XCTAssertEqual(actualKey, expectedKey)
|
||||
XCTAssertEqual(actualValue, expectedValue)
|
||||
|
||||
let actual = orderedDictionary
|
||||
let expected: OrderedDictionary<String, Int> = ["B": 2, "C": 3, "D": 4]
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testRemoveLastNonEmpty() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3, "D": 4]
|
||||
|
||||
let (actualKey, actualValue) = orderedDictionary.removeLast()
|
||||
let (expectedKey, expectedValue) = ("D", 4)
|
||||
|
||||
XCTAssertEqual(actualKey, expectedKey)
|
||||
XCTAssertEqual(actualValue, expectedValue)
|
||||
|
||||
let actual = orderedDictionary
|
||||
let expected: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3]
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Moving Elements
|
||||
// ======================================================= //
|
||||
|
||||
func testMovingElements() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3, "D": 4]
|
||||
|
||||
XCTAssertEqual(orderedDictionary.moveElement(forKey: "A", to: 2), 0) // B, C, A, D
|
||||
XCTAssertEqual(orderedDictionary.moveElement(forKey: "D", to: 3), 3) // B, C, A, D
|
||||
XCTAssertEqual(orderedDictionary.moveElement(forKey: "C", to: 0), 1) // C, B, A, D
|
||||
XCTAssertEqual(orderedDictionary.moveElement(forKey: "B", to: 3), 1) // C, A, D, B
|
||||
XCTAssertEqual(orderedDictionary.moveElement(forKey: "E", to: 0), nil)
|
||||
|
||||
let actual = orderedDictionary
|
||||
let expected: OrderedDictionary<String, Int> = ["C": 3, "A": 1, "D": 4, "B": 2]
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Sorting Elements
|
||||
// ======================================================= //
|
||||
|
||||
func testSortingWithMutation() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["E": 4, "G": 3, "A": 3, "D": 1, "B": 4]
|
||||
orderedDictionary.sort { element1, element2 in
|
||||
if element1.value != element2.value { return element1.value < element2.value }
|
||||
return element1.key < element2.key
|
||||
}
|
||||
let actual = orderedDictionary
|
||||
|
||||
let expected: OrderedDictionary<String, Int> = ["D": 1, "A": 3, "G": 3, "B": 4, "E": 4]
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testSortingWithoutMutation() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["E": 4, "G": 3, "A": 3, "D": 1, "B": 4]
|
||||
let actual: OrderedDictionary<String, Int> = orderedDictionary.sorted { element1, element2 in
|
||||
if element1.value != element2.value { return element1.value < element2.value }
|
||||
return element1.key < element2.key
|
||||
}
|
||||
|
||||
let expected: OrderedDictionary<String, Int> = ["D": 1, "A": 3, "G": 3, "B": 4, "E": 4]
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Mapping Values
|
||||
// ======================================================= //
|
||||
|
||||
func testMapValues() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3, "D": 4]
|
||||
|
||||
let actual = orderedDictionary.mapValues { String($0) }
|
||||
let expected: OrderedDictionary<String, String> = ["A": "1", "B": "2", "C": "3", "D": "4"]
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testCompactMapValues() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3, "D": 4]
|
||||
|
||||
let actual = orderedDictionary.compactMapValues { $0 % 2 == 0 ? String($0) : nil }
|
||||
let expected: OrderedDictionary<String, String> = ["B": "2", "D": "4"]
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Slices
|
||||
// ======================================================= //
|
||||
|
||||
func testSliceAccess() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["A": 1, "B": 2, "C": 3, "D": 4]
|
||||
let slice = orderedDictionary[2..<4]
|
||||
|
||||
XCTAssertEqual(slice.count, 2)
|
||||
XCTAssertEqual(slice.startIndex, 2)
|
||||
XCTAssertEqual(slice.endIndex, 4)
|
||||
XCTAssertEqual(Array(slice.indices), [2, 3])
|
||||
XCTAssert(slice[2] == (key: "C", value: 3))
|
||||
XCTAssert(slice[3] == (key: "D", value: 4))
|
||||
}
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Capacity
|
||||
// ============================================================================ //
|
||||
|
||||
func testCapacityReservationViaInit() {
|
||||
let orderedDictionary = OrderedDictionary<String, Int>(minimumCapacity: 10)
|
||||
|
||||
XCTAssertGreaterThanOrEqual(orderedDictionary.capacity, 10)
|
||||
}
|
||||
|
||||
func testCapacityReservationViaMethod() {
|
||||
var orderedDictionary = OrderedDictionary<String, Int>()
|
||||
|
||||
XCTAssertEqual(orderedDictionary.capacity, 0)
|
||||
|
||||
orderedDictionary.reserveCapacity(10)
|
||||
|
||||
XCTAssertGreaterThanOrEqual(orderedDictionary.capacity, 10)
|
||||
XCTAssertLessThan(orderedDictionary.capacity, 20)
|
||||
|
||||
orderedDictionary.reserveCapacity(20)
|
||||
|
||||
XCTAssertGreaterThanOrEqual(orderedDictionary.capacity, 20)
|
||||
}
|
||||
|
||||
func testCapacityGrowForElementInsertion() {
|
||||
var orderedDictionary = OrderedDictionary<String, Int>()
|
||||
|
||||
XCTAssertEqual(orderedDictionary.capacity, 0)
|
||||
|
||||
orderedDictionary["A"] = 1
|
||||
|
||||
XCTAssertEqual(orderedDictionary.capacity, 1)
|
||||
|
||||
orderedDictionary["B"] = 2
|
||||
orderedDictionary["A"] = 3
|
||||
|
||||
XCTAssertEqual(orderedDictionary.capacity, 2)
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Codable
|
||||
// ======================================================= //
|
||||
|
||||
func testEncodingAndDecodingViaJSON() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = [
|
||||
"A": 42,
|
||||
"B": 100,
|
||||
"C": 11
|
||||
]
|
||||
|
||||
let jsonEncoder = JSONEncoder()
|
||||
let data = try! jsonEncoder.encode(orderedDictionary)
|
||||
let actualString = String(data: data, encoding: .utf8)
|
||||
|
||||
let expectedString = "[\"A\",42,\"B\",100,\"C\",11]"
|
||||
|
||||
XCTAssertEqual(actualString, expectedString)
|
||||
|
||||
let jsonDecoder = JSONDecoder()
|
||||
let actual = try! jsonDecoder.decode(OrderedDictionary<String, Int>.self, from: data)
|
||||
|
||||
let expected = orderedDictionary
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testEncodingAndDecodingViaPropertyList() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = [
|
||||
"A": 42,
|
||||
"B": 100,
|
||||
"C": 11
|
||||
]
|
||||
|
||||
let plistEncoder = PropertyListEncoder()
|
||||
let data = try! plistEncoder.encode(orderedDictionary)
|
||||
|
||||
let plistDecoder = PropertyListDecoder()
|
||||
let actual = try! plistDecoder.decode(OrderedDictionary<String, Int>.self, from: data)
|
||||
|
||||
let expected = orderedDictionary
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Description
|
||||
// ======================================================= //
|
||||
|
||||
struct DescribedValue: CustomStringConvertible, CustomDebugStringConvertible {
|
||||
init(_ value: Int) { self.value = value }
|
||||
let value: Int
|
||||
var description: String { return "\(value)" }
|
||||
var debugDescription: String { return "debug(\(value))" }
|
||||
}
|
||||
|
||||
func testEmptyDescription() {
|
||||
let orderedDictionary = OrderedDictionary<String, DescribedValue>()
|
||||
let actual = orderedDictionary.description
|
||||
|
||||
let expected = "[:]"
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testDescription() {
|
||||
let orderedDictionary: OrderedDictionary<String, DescribedValue> = [
|
||||
"A": DescribedValue(1),
|
||||
"B": DescribedValue(2),
|
||||
"C": DescribedValue(3)
|
||||
]
|
||||
let actual = orderedDictionary.description
|
||||
|
||||
let expected = "[A: 1, B: 2, C: 3]"
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testEmptyDebugDescription() {
|
||||
let orderedDictionary = OrderedDictionary<String, DescribedValue>()
|
||||
let actual = orderedDictionary.debugDescription
|
||||
|
||||
let expected = "[:]"
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testDebugDescription() {
|
||||
let orderedDictionary: OrderedDictionary<String, DescribedValue> = [
|
||||
"A": DescribedValue(1),
|
||||
"B": DescribedValue(2),
|
||||
"C": DescribedValue(3)
|
||||
]
|
||||
let actual = orderedDictionary.debugDescription
|
||||
|
||||
let expected = "[\"A\": debug(1), \"B\": debug(2), \"C\": debug(3)]"
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import Foundation
|
||||
import OrderedDictionary
|
||||
import XCTest
|
||||
|
||||
class AccessTests: XCTestCase {
|
||||
|
||||
func testAccessBasic() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
XCTAssertEqual(orderedDictionary.count, 3)
|
||||
XCTAssertFalse(orderedDictionary.isEmpty)
|
||||
|
||||
XCTAssertEqual(orderedDictionary.indices, 0..<3)
|
||||
XCTAssertEqual(orderedDictionary.startIndex, 0)
|
||||
XCTAssertEqual(orderedDictionary.endIndex, 3)
|
||||
|
||||
XCTAssertEqual(orderedDictionary[0].key, "a")
|
||||
XCTAssertEqual(orderedDictionary[1].key, "b")
|
||||
XCTAssertEqual(orderedDictionary[2].key, "c")
|
||||
|
||||
XCTAssertTrue(orderedDictionary.containsKey("a"))
|
||||
XCTAssertTrue(orderedDictionary.containsKey("b"))
|
||||
XCTAssertTrue(orderedDictionary.containsKey("c"))
|
||||
|
||||
XCTAssertEqual(orderedDictionary["a"], 1)
|
||||
XCTAssertEqual(orderedDictionary["b"], 2)
|
||||
XCTAssertEqual(orderedDictionary["c"], 3)
|
||||
|
||||
XCTAssertEqual(orderedDictionary.value(forKey: "a"), 1)
|
||||
XCTAssertEqual(orderedDictionary.value(forKey: "b"), 2)
|
||||
XCTAssertEqual(orderedDictionary.value(forKey: "c"), 3)
|
||||
|
||||
XCTAssertEqual(orderedDictionary.index(forKey: "a"), 0)
|
||||
XCTAssertEqual(orderedDictionary.index(forKey: "b"), 1)
|
||||
XCTAssertEqual(orderedDictionary.index(forKey: "c"), 2)
|
||||
|
||||
XCTAssertNotNil(orderedDictionary.elementAt(0))
|
||||
XCTAssertNotNil(orderedDictionary.elementAt(1))
|
||||
XCTAssertNotNil(orderedDictionary.elementAt(2))
|
||||
}
|
||||
|
||||
func testAccessSlice() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3, "d": 4]
|
||||
let slice = orderedDictionary[2..<4]
|
||||
|
||||
XCTAssertEqual(slice.count, 2)
|
||||
XCTAssertFalse(slice.isEmpty)
|
||||
|
||||
XCTAssertEqual(slice.indices, 2..<4)
|
||||
XCTAssertEqual(slice.startIndex, 2)
|
||||
XCTAssertEqual(slice.endIndex, 4)
|
||||
|
||||
XCTAssertEqual(orderedDictionary[2].key, "c")
|
||||
XCTAssertEqual(orderedDictionary[3].key, "d")
|
||||
}
|
||||
|
||||
func testAccessOrderedKeys() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
XCTAssertEqual(
|
||||
orderedDictionary.orderedKeys,
|
||||
["a", "b", "c"]
|
||||
)
|
||||
}
|
||||
|
||||
func testAccessOrderedValues() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
XCTAssertEqual(
|
||||
Array(orderedDictionary.orderedValues),
|
||||
[1, 2, 3]
|
||||
)
|
||||
}
|
||||
|
||||
func testAccessElementAtInvalidIndex() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
XCTAssertNil(orderedDictionary.elementAt(-1))
|
||||
XCTAssertNil(orderedDictionary.elementAt(3))
|
||||
}
|
||||
|
||||
func testAccessUnsortedDictionary() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
XCTAssertEqual(
|
||||
orderedDictionary.unorderedDictionary,
|
||||
["a": 1, "b": 2, "c": 3]
|
||||
)
|
||||
}
|
||||
|
||||
func testIteratorInForLoop() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
var keys = [String]()
|
||||
var values = [Int]()
|
||||
|
||||
for (key, value) in orderedDictionary {
|
||||
keys.append(key)
|
||||
values.append(value)
|
||||
}
|
||||
|
||||
XCTAssertEqual(keys, ["a", "b", "c"])
|
||||
XCTAssertEqual(values, [1, 2, 3])
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import Foundation
|
||||
import OrderedDictionary
|
||||
import XCTest
|
||||
|
||||
class CapacityTests: XCTestCase {
|
||||
|
||||
func testCapacityReservationViaInit() {
|
||||
let orderedDictionary = OrderedDictionary<String, Int>(minimumCapacity: 10)
|
||||
|
||||
XCTAssertGreaterThanOrEqual(orderedDictionary.capacity, 10)
|
||||
}
|
||||
|
||||
func testCapacityReservationViaMethod() {
|
||||
var orderedDictionary = OrderedDictionary<String, Int>()
|
||||
|
||||
XCTAssertEqual(orderedDictionary.capacity, 0)
|
||||
|
||||
orderedDictionary.reserveCapacity(10)
|
||||
|
||||
XCTAssertGreaterThanOrEqual(orderedDictionary.capacity, 10)
|
||||
XCTAssertLessThan(orderedDictionary.capacity, 20)
|
||||
|
||||
orderedDictionary.reserveCapacity(20)
|
||||
|
||||
XCTAssertGreaterThanOrEqual(orderedDictionary.capacity, 20)
|
||||
}
|
||||
|
||||
func testCapacityGrowForElementInsertion() {
|
||||
var orderedDictionary = OrderedDictionary<String, Int>()
|
||||
|
||||
XCTAssertEqual(orderedDictionary.capacity, 0)
|
||||
|
||||
orderedDictionary["a"] = 1
|
||||
|
||||
XCTAssertEqual(orderedDictionary.capacity, 1)
|
||||
|
||||
orderedDictionary["b"] = 2
|
||||
orderedDictionary["a"] = 3
|
||||
|
||||
XCTAssertEqual(orderedDictionary.capacity, 2)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import Foundation
|
||||
import OrderedDictionary
|
||||
import XCTest
|
||||
|
||||
class CodingTests: XCTestCase {
|
||||
|
||||
func testEncodingAndDecodingViaJSON() throws {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
let jsonEncoder = JSONEncoder()
|
||||
let data = try jsonEncoder.encode(orderedDictionary)
|
||||
let actualString = String(data: data, encoding: .utf8)
|
||||
|
||||
let expectedString = "[\"a\",1,\"b\",2,\"c\",3]"
|
||||
|
||||
XCTAssertEqual(actualString, expectedString)
|
||||
|
||||
let jsonDecoder = JSONDecoder()
|
||||
let actual = try jsonDecoder.decode(OrderedDictionary<String, Int>.self, from: data)
|
||||
|
||||
let expected = orderedDictionary
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testEncodingAndDecodingViaPropertyList() throws {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
let plistEncoder = PropertyListEncoder()
|
||||
let data = try plistEncoder.encode(orderedDictionary)
|
||||
|
||||
let plistDecoder = PropertyListDecoder()
|
||||
let actual = try plistDecoder.decode(OrderedDictionary<String, Int>.self, from: data)
|
||||
|
||||
let expected = orderedDictionary
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import Foundation
|
||||
import OrderedDictionary
|
||||
import XCTest
|
||||
|
||||
struct DescribedValue: CustomStringConvertible, CustomDebugStringConvertible {
|
||||
init(_ value: Int) { self.value = value }
|
||||
let value: Int
|
||||
var description: String { return "\(value)" }
|
||||
var debugDescription: String { return "debug(\(value))" }
|
||||
}
|
||||
|
||||
class DescriptionTests: XCTestCase {
|
||||
|
||||
func testEmptyDescription() {
|
||||
let orderedDictionary = OrderedDictionary<String, DescribedValue>()
|
||||
|
||||
XCTAssertEqual(orderedDictionary.description, "[:]")
|
||||
}
|
||||
|
||||
func testDescription() {
|
||||
let orderedDictionary: OrderedDictionary = [
|
||||
"a": DescribedValue(1),
|
||||
"b": DescribedValue(2),
|
||||
"c": DescribedValue(3)
|
||||
]
|
||||
|
||||
XCTAssertEqual(orderedDictionary.description, "[a: 1, b: 2, c: 3]")
|
||||
}
|
||||
|
||||
func testEmptyDebugDescription() {
|
||||
let orderedDictionary = OrderedDictionary<String, DescribedValue>()
|
||||
|
||||
XCTAssertEqual(orderedDictionary.debugDescription, "[:]")
|
||||
}
|
||||
|
||||
func testDebugDescription() {
|
||||
let orderedDictionary: OrderedDictionary = [
|
||||
"a": DescribedValue(1),
|
||||
"b": DescribedValue(2),
|
||||
"c": DescribedValue(3)
|
||||
]
|
||||
|
||||
XCTAssertEqual(
|
||||
orderedDictionary.debugDescription,
|
||||
"[\"a\": debug(1), \"b\": debug(2), \"c\": debug(3)]"
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import Foundation
|
||||
import OrderedDictionary
|
||||
import XCTest
|
||||
|
||||
class InitializationTests: XCTestCase {
|
||||
|
||||
func testInitializationFromArrayLiteral() {
|
||||
let actual: OrderedDictionary = [
|
||||
("a", 1),
|
||||
("b", 2),
|
||||
("c", 3)
|
||||
]
|
||||
|
||||
let expected = OrderedDictionary(uniqueKeysWithValues: [
|
||||
(key: "a", value: 1),
|
||||
(key: "b", value: 2),
|
||||
(key: "c", value: 3)
|
||||
])
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testInitializationFromDictionaryLiteral() {
|
||||
let actual: OrderedDictionary = [
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3
|
||||
]
|
||||
|
||||
let expected = OrderedDictionary(uniqueKeysWithValues: [
|
||||
(key: "a", value: 1),
|
||||
(key: "b", value: 2),
|
||||
(key: "c", value: 3)
|
||||
])
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testInitializationFromValuesAndKeyProviderClosure() {
|
||||
let actual = OrderedDictionary(
|
||||
values: [1, 2, 3],
|
||||
uniquelyKeyedBy: { String($0) }
|
||||
)
|
||||
|
||||
let expected = OrderedDictionary(uniqueKeysWithValues: [
|
||||
(key: "1", value: 1),
|
||||
(key: "2", value: 2),
|
||||
(key: "3", value: 3)
|
||||
])
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testInitializationFromValuesAndKeyPath() {
|
||||
let actual = OrderedDictionary(
|
||||
values: ["a", "b", "c"],
|
||||
uniquelyKeyedBy: \.self
|
||||
)
|
||||
|
||||
let expected = OrderedDictionary(uniqueKeysWithValues: [
|
||||
(key: "a", value: "a"),
|
||||
(key: "b", value: "b"),
|
||||
(key: "c", value: "c")
|
||||
])
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testInitializationFromUnsortedDictionaryAndSortFunction() {
|
||||
let actual = OrderedDictionary(
|
||||
unsorted: [
|
||||
2: "foo",
|
||||
1: "bar",
|
||||
4: "baz",
|
||||
5: "bat",
|
||||
3: "bam"
|
||||
],
|
||||
areInIncreasingOrder: { $0.key < $1.key }
|
||||
)
|
||||
|
||||
let expected = OrderedDictionary(uniqueKeysWithValues: [
|
||||
(key: 1, value: "bar"),
|
||||
(key: 2, value: "foo"),
|
||||
(key: 3, value: "bam"),
|
||||
(key: 4, value: "baz"),
|
||||
(key: 5, value: "bat")
|
||||
])
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import Foundation
|
||||
import OrderedDictionary
|
||||
import XCTest
|
||||
|
||||
class InsertionsTests: XCTestCase {
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Index-based Insertion
|
||||
// ============================================================================ //
|
||||
|
||||
func testIndexBasedInsertion_uniqueKey_startIndex() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
let newElement = (key: "d", value: 4)
|
||||
orderedDictionary.insert(newElement, at: 0)
|
||||
XCTAssertEqual(orderedDictionary, ["d": 4, "a": 1, "b": 2, "c": 3])
|
||||
}
|
||||
|
||||
func testIndexBasedInsertion_uniqueKey_middleIndex() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
let newElement = (key: "d", value: 4)
|
||||
orderedDictionary.insert(newElement, at: 2)
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "b": 2, "d": 4, "c": 3])
|
||||
}
|
||||
|
||||
func testIndexBasedInsertion_uniqueKey_endIndex() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
let newElement = (key: "d", value: 4)
|
||||
orderedDictionary.insert(newElement, at: 3)
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "b": 2, "c": 3, "d": 4])
|
||||
}
|
||||
|
||||
func testIndexBasedInsertion_duplicateKey() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
XCTAssertFalse(orderedDictionary.canInsert(key: "a"))
|
||||
XCTAssertTrue(orderedDictionary.canInsert(key: "d"))
|
||||
}
|
||||
|
||||
func testIndexBasedInsertion_invalidIndex() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
XCTAssertFalse(orderedDictionary.canInsert(at: -1))
|
||||
XCTAssertFalse(orderedDictionary.canInsert(at: 4))
|
||||
}
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Key-based Insertion
|
||||
// ============================================================================ //
|
||||
|
||||
func testKeyBasedInsertion() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
orderedDictionary["d"] = 4
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "b": 2, "c": 3, "d": 4])
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import Foundation
|
||||
import OrderedDictionary
|
||||
import XCTest
|
||||
|
||||
class MapFilterTests: XCTestCase {
|
||||
|
||||
func testMapValues() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3, "d": 4]
|
||||
|
||||
XCTAssertEqual(
|
||||
orderedDictionary.mapValues { String($0) },
|
||||
["a": "1", "b": "2", "c": "3", "d": "4"]
|
||||
)
|
||||
}
|
||||
|
||||
func testCompactMapValues() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3, "d": 4]
|
||||
|
||||
XCTAssertEqual(
|
||||
orderedDictionary.compactMapValues { $0.isMultiple(of: 2) ? String($0) : nil },
|
||||
["b": "2", "d": "4"]
|
||||
)
|
||||
}
|
||||
|
||||
func testFilter() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3, "d": 4]
|
||||
|
||||
XCTAssertEqual(
|
||||
orderedDictionary.filter { $0.value.isMultiple(of: 2) },
|
||||
["b": 2, "d": 4]
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
import Foundation
|
||||
import OrderedDictionary
|
||||
import XCTest
|
||||
|
||||
class RemovalTests: XCTestCase {
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Key-based Removal Via removeValue(forKey:)
|
||||
// ============================================================================ //
|
||||
|
||||
func testKeyBasedRemoval_viaMethod_existingKey() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
let value = orderedDictionary.removeValue(forKey: "b")
|
||||
|
||||
XCTAssertEqual(value, 2)
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "c": 3])
|
||||
}
|
||||
|
||||
func testKeyBasedRemoval_viaMethod_invalidKey() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
let value = orderedDictionary.removeValue(forKey: "d")
|
||||
|
||||
XCTAssertNil(value)
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "b": 2, "c": 3])
|
||||
}
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Key-based Removal Via subscript(key:)
|
||||
// ============================================================================ //
|
||||
|
||||
func testKeyBasedRemoval_viaSubscript_existingKey() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
orderedDictionary["b"] = nil
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "c": 3])
|
||||
}
|
||||
|
||||
func testKeyBasedRemoval_viaSubscript_invalidKey() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
orderedDictionary["d"] = nil
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "b": 2, "c": 3])
|
||||
}
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Index-based Removal Via Method
|
||||
// ============================================================================ //
|
||||
|
||||
func testIndexBasedRemoval_viaMethod_validIndex() throws {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
let (key, value) = try XCTUnwrap(orderedDictionary.remove(at: 2))
|
||||
|
||||
XCTAssertEqual(key, "c")
|
||||
XCTAssertEqual(value, 3)
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "b": 2])
|
||||
}
|
||||
|
||||
func testIndexBasedRemoval_viaMethod_invalidIndex() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
let element = orderedDictionary.remove(at: 5)
|
||||
|
||||
XCTAssertNil(element)
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "b": 2, "c": 3])
|
||||
}
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Remove All
|
||||
// ============================================================================ //
|
||||
|
||||
func testRemoveAll() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
orderedDictionary.removeAll()
|
||||
|
||||
XCTAssertEqual(orderedDictionary, [])
|
||||
}
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Pop First & Last
|
||||
// ============================================================================ //
|
||||
|
||||
func testPopFirstEmpty() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = []
|
||||
|
||||
let first = orderedDictionary.popFirst()
|
||||
|
||||
XCTAssertNil(first)
|
||||
}
|
||||
|
||||
func testPopFirstNonEmpty() throws {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3, "d": 4]
|
||||
|
||||
let first = try XCTUnwrap(orderedDictionary.popFirst())
|
||||
|
||||
XCTAssertEqual(first.key, "a")
|
||||
XCTAssertEqual(first.value, 1)
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["b": 2, "c": 3, "d": 4])
|
||||
}
|
||||
|
||||
func testPopLastEmpty() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = []
|
||||
|
||||
let last = orderedDictionary.popLast()
|
||||
|
||||
XCTAssertNil(last)
|
||||
}
|
||||
|
||||
func testPopLastNonEmpty() throws {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3, "d": 4]
|
||||
|
||||
let last = try XCTUnwrap(orderedDictionary.popLast())
|
||||
|
||||
XCTAssertEqual(last.key, "d")
|
||||
XCTAssertEqual(last.value, 4)
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "b": 2, "c": 3])
|
||||
}
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Removal First & Last
|
||||
// ============================================================================ //
|
||||
|
||||
func testRemoveFirstNonEmpty() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3, "d": 4]
|
||||
|
||||
let first = orderedDictionary.removeFirst()
|
||||
|
||||
XCTAssertEqual(first.key, "a")
|
||||
XCTAssertEqual(first.value, 1)
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["b": 2, "c": 3, "d": 4])
|
||||
}
|
||||
|
||||
func testRemoveLastNonEmpty() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3, "d": 4]
|
||||
|
||||
let last = orderedDictionary.removeLast()
|
||||
|
||||
XCTAssertEqual(last.key, "d")
|
||||
XCTAssertEqual(last.value, 4)
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "b": 2, "c": 3])
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import Foundation
|
||||
import OrderedDictionary
|
||||
import XCTest
|
||||
|
||||
class ReorderingTests: XCTestCase {
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Reversal
|
||||
// ============================================================================ //
|
||||
|
||||
func testReversal() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3, "d": 4]
|
||||
|
||||
orderedDictionary.reverse()
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["d": 4, "c": 3, "b": 2, "a": 1])
|
||||
}
|
||||
|
||||
func testReversal_throughSlice() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3, "d": 4]
|
||||
|
||||
orderedDictionary[1..<3].reverse()
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "c": 3, "b": 2, "d": 4])
|
||||
}
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Partitioning
|
||||
// ============================================================================ //
|
||||
|
||||
func testPartitioning() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3, "d": 4]
|
||||
|
||||
let index = orderedDictionary.partition(by: { $0.value.isMultiple(of: 2) })
|
||||
|
||||
XCTAssertEqual(index, 2)
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "c": 3, "b": 2, "d": 4])
|
||||
}
|
||||
|
||||
func testPartitioning_throughSlice() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3, "d": 4]
|
||||
|
||||
let index = orderedDictionary[0..<3].partition(by: { !$0.value.isMultiple(of: 2) })
|
||||
|
||||
XCTAssertEqual(index, 1)
|
||||
XCTAssertEqual(orderedDictionary, ["b": 2, "a": 1, "c": 3, "d": 4])
|
||||
}
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Swapping
|
||||
// ============================================================================ //
|
||||
|
||||
func testSwapAtDifferentIndices() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3, "d": 4]
|
||||
|
||||
orderedDictionary.swapAt(1, 3)
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "d": 4, "c": 3, "b": 2])
|
||||
}
|
||||
|
||||
func testSwapAtSameIndex() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3, "d": 4]
|
||||
|
||||
orderedDictionary.swapAt(0, 0)
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "b": 2, "c": 3, "d": 4])
|
||||
}
|
||||
|
||||
func testSwapAtDifferentIndices_throughSlice() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3, "d": 4]
|
||||
|
||||
orderedDictionary[1..<4].swapAt(1, 3)
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "d": 4, "c": 3, "b": 2])
|
||||
}
|
||||
|
||||
func testSwapAtSameIndex_throughSlice() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3, "d": 4]
|
||||
|
||||
orderedDictionary[0..<1].swapAt(0, 0)
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "b": 2, "c": 3, "d": 4])
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import Foundation
|
||||
import OrderedDictionary
|
||||
import XCTest
|
||||
|
||||
func sortByValuesAndKeys<Key: Comparable, Value: Comparable>(
|
||||
element1: (key: Key, value: Value),
|
||||
element2: (key: Key, value: Value)
|
||||
) -> Bool {
|
||||
if element1.value != element2.value {
|
||||
return element1.value < element2.value
|
||||
} else {
|
||||
return element1.key < element2.key
|
||||
}
|
||||
}
|
||||
|
||||
class SortingTests: XCTestCase {
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Mutating Sort
|
||||
// ============================================================================ //
|
||||
|
||||
func testMutatingSort() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["e": 4, "g": 3, "a": 3, "d": 1, "b": 4]
|
||||
|
||||
orderedDictionary.sort(by: sortByValuesAndKeys)
|
||||
|
||||
XCTAssertEqual(
|
||||
orderedDictionary,
|
||||
["d": 1, "a": 3, "g": 3, "b": 4, "e": 4]
|
||||
)
|
||||
}
|
||||
|
||||
func testMutatingSort_throughSlice() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["e": 4, "g": 3, "a": 3, "d": 1, "b": 4]
|
||||
|
||||
orderedDictionary[2..<5].sort(by: sortByValuesAndKeys)
|
||||
|
||||
XCTAssertEqual(
|
||||
orderedDictionary,
|
||||
["e": 4, "g": 3, "d": 1, "a": 3, "b": 4]
|
||||
)
|
||||
}
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Non-mutating Sort
|
||||
// ============================================================================ //
|
||||
|
||||
func testSortingWithoutMutation() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["e": 4, "g": 3, "a": 3, "d": 1, "b": 4]
|
||||
|
||||
XCTAssertEqual(
|
||||
orderedDictionary.sorted(by: sortByValuesAndKeys),
|
||||
["d": 1, "a": 3, "g": 3, "b": 4, "e": 4]
|
||||
)
|
||||
}
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Sorting Unsorted Dictionary
|
||||
// ============================================================================ //
|
||||
|
||||
func testSortingUnsortedDictionary() {
|
||||
let dictionary = [
|
||||
2: "foo",
|
||||
1: "bar",
|
||||
4: "baz",
|
||||
5: "bat",
|
||||
3: "bam"
|
||||
]
|
||||
|
||||
let actual = dictionary.sorted { $0.key < $1.key }
|
||||
|
||||
let expected: OrderedDictionary<Int, String> = [
|
||||
1: "bar",
|
||||
2: "foo",
|
||||
3: "bam",
|
||||
4: "baz",
|
||||
5: "bat"
|
||||
]
|
||||
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import Foundation
|
||||
import OrderedDictionary
|
||||
import XCTest
|
||||
|
||||
// See #49
|
||||
class SubscriptAmbiguityTests: XCTestCase {
|
||||
|
||||
func testAccess() {
|
||||
let orderedDictionary: OrderedDictionary<Int, String> = [1: "a", 2: "b", 3: "c"]
|
||||
|
||||
let valueForKey = orderedDictionary[1] as String?
|
||||
let elementAtIndex = orderedDictionary[1] as (key: Int, value: String)
|
||||
|
||||
XCTAssertEqual(valueForKey, "a")
|
||||
XCTAssertEqual(elementAtIndex.key, 2)
|
||||
XCTAssertEqual(elementAtIndex.value, "b")
|
||||
}
|
||||
|
||||
func testIndexBasedUpdate() {
|
||||
var orderedDictionary: OrderedDictionary<Int, String> = [1: "a", 2: "b", 3: "c"]
|
||||
|
||||
orderedDictionary[1] = (key: 2, value: "x")
|
||||
|
||||
XCTAssertEqual(orderedDictionary, [1: "a", 2: "x", 3: "c"])
|
||||
}
|
||||
|
||||
func testKeyBasedUpdate() {
|
||||
var orderedDictionary: OrderedDictionary<Int, String> = [1: "a", 2: "b", 3: "c"]
|
||||
|
||||
orderedDictionary[1] = "x"
|
||||
|
||||
XCTAssertEqual(orderedDictionary, [1: "x", 2: "b", 3: "c"])
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import Foundation
|
||||
import OrderedDictionary
|
||||
import XCTest
|
||||
|
||||
class UpdatesTests: XCTestCase {
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Index-based Updates Via update(_:at:)
|
||||
// ============================================================================ //
|
||||
|
||||
func testIndexBasedUpdate_viaMethod_sameKey() throws {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
let newElement = (key: "b", value: 0)
|
||||
let previousElement = try XCTUnwrap(orderedDictionary.update(newElement, at: 1))
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "b": 0, "c": 3])
|
||||
|
||||
XCTAssertEqual(previousElement.key, "b")
|
||||
XCTAssertEqual(previousElement.value, 2)
|
||||
}
|
||||
|
||||
func testIndexBasedUpdate_viaMethod_newUniqueKey() throws {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
let newElement = (key: "d", value: 0)
|
||||
let previousElement = try XCTUnwrap(orderedDictionary.update(newElement, at: 1))
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "d": 0, "c": 3])
|
||||
|
||||
XCTAssertEqual(previousElement.key, "b")
|
||||
XCTAssertEqual(previousElement.value, 2)
|
||||
}
|
||||
|
||||
func testIndexBasedUpdate_viaMethod_duplicateKey() {
|
||||
let orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
let element = (key: "a", value: 42)
|
||||
|
||||
XCTAssertTrue(orderedDictionary.canUpdate(element, at: 0))
|
||||
XCTAssertFalse(orderedDictionary.canUpdate(element, at: 1))
|
||||
XCTAssertFalse(orderedDictionary.canUpdate(element, at: 2))
|
||||
}
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Index-based Updates Via subscript(position:)
|
||||
// ============================================================================ //
|
||||
|
||||
func testIndexBasedUpdate_viaSubscriptSingle_sameKey() throws {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
orderedDictionary[1] = (key: "b", value: 0)
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "b": 0, "c": 3])
|
||||
}
|
||||
|
||||
func testIndexBasedUpdate_viaSubscriptSingle_newUniqueKey() throws {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
orderedDictionary[1] = (key: "d", value: 0)
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "d": 0, "c": 3])
|
||||
}
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Index-based Updates Via subscript(bounds:)
|
||||
// ============================================================================ //
|
||||
|
||||
func testIndexBasedUpdate_viaSubscriptMultiple_newUniqueKeys() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
let sliceOrderedDictionary: OrderedDictionary<String, Int> = ["d": 0, "e": 0]
|
||||
let slice = sliceOrderedDictionary[0..<2]
|
||||
|
||||
orderedDictionary[1..<3] = slice
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "d": 0, "e": 0])
|
||||
}
|
||||
|
||||
func testIndexBasedUpdate_viaSubscriptMultiple_sameKeys() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
let sliceOrderedDictionary: OrderedDictionary<String, Int> = ["c": 0, "b": 0]
|
||||
let slice = sliceOrderedDictionary[0..<2]
|
||||
|
||||
orderedDictionary[1..<3] = slice
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "c": 0, "b": 0])
|
||||
}
|
||||
|
||||
func testIndexBasedUpdate_viaSubscriptMultiple_mixedKeys() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
let sliceOrderedDictionary: OrderedDictionary<String, Int> = ["d": 0, "c": 0]
|
||||
let slice = sliceOrderedDictionary[0..<2]
|
||||
|
||||
orderedDictionary[1..<3] = slice
|
||||
|
||||
XCTAssertEqual(orderedDictionary, ["a": 1, "d": 0, "c": 0])
|
||||
}
|
||||
|
||||
// ============================================================================ //
|
||||
// MARK: - Key-based Updates
|
||||
// ============================================================================ //
|
||||
|
||||
func testKeyBasedUpdate() {
|
||||
var orderedDictionary: OrderedDictionary<String, Int> = ["a": 1, "b": 2, "c": 3]
|
||||
|
||||
// Update
|
||||
orderedDictionary["a"] = 5
|
||||
XCTAssertEqual(orderedDictionary, ["a": 5, "b": 2, "c": 3])
|
||||
|
||||
// Insertion
|
||||
orderedDictionary["d"] = 10
|
||||
XCTAssertEqual(orderedDictionary, ["a": 5, "b": 2, "c": 3, "d": 10])
|
||||
XCTAssertTrue(orderedDictionary.containsKey("d"))
|
||||
|
||||
// Removal
|
||||
orderedDictionary["b"] = nil
|
||||
XCTAssertEqual(orderedDictionary, ["a": 5, "c": 3, "d": 10])
|
||||
XCTAssertFalse(orderedDictionary.containsKey("b"))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// https://github.com/realm/SwiftLint/pull/2965
|
||||
// https://bugs.swift.org/browse/SR-11501
|
||||
#if compiler(<5.1) || (SWIFT_PACKAGE && os(macOS))
|
||||
internal enum UnwrapError: Error {
|
||||
case missingValue
|
||||
}
|
||||
|
||||
internal func XCTUnwrap<T>(
|
||||
_ expression: @autoclosure () throws -> T?,
|
||||
_ message: @autoclosure () -> String = ""
|
||||
) throws -> T {
|
||||
if let value = try expression() {
|
||||
return value
|
||||
} else {
|
||||
throw UnwrapError.missingValue
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,183 @@
|
||||
#if !canImport(ObjectiveC)
|
||||
import XCTest
|
||||
|
||||
extension AccessTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__AccessTests = [
|
||||
("testAccessBasic", testAccessBasic),
|
||||
("testAccessElementAtInvalidIndex", testAccessElementAtInvalidIndex),
|
||||
("testAccessOrderedKeys", testAccessOrderedKeys),
|
||||
("testAccessOrderedValues", testAccessOrderedValues),
|
||||
("testAccessSlice", testAccessSlice),
|
||||
("testAccessUnsortedDictionary", testAccessUnsortedDictionary),
|
||||
("testIteratorInForLoop", testIteratorInForLoop),
|
||||
]
|
||||
}
|
||||
|
||||
extension CapacityTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__CapacityTests = [
|
||||
("testCapacityGrowForElementInsertion", testCapacityGrowForElementInsertion),
|
||||
("testCapacityReservationViaInit", testCapacityReservationViaInit),
|
||||
("testCapacityReservationViaMethod", testCapacityReservationViaMethod),
|
||||
]
|
||||
}
|
||||
|
||||
extension CodingTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__CodingTests = [
|
||||
("testEncodingAndDecodingViaJSON", testEncodingAndDecodingViaJSON),
|
||||
("testEncodingAndDecodingViaPropertyList", testEncodingAndDecodingViaPropertyList),
|
||||
]
|
||||
}
|
||||
|
||||
extension DescriptionTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__DescriptionTests = [
|
||||
("testDebugDescription", testDebugDescription),
|
||||
("testDescription", testDescription),
|
||||
("testEmptyDebugDescription", testEmptyDebugDescription),
|
||||
("testEmptyDescription", testEmptyDescription),
|
||||
]
|
||||
}
|
||||
|
||||
extension InitializationTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__InitializationTests = [
|
||||
("testInitializationFromArrayLiteral", testInitializationFromArrayLiteral),
|
||||
("testInitializationFromDictionaryLiteral", testInitializationFromDictionaryLiteral),
|
||||
("testInitializationFromUnsortedDictionaryAndSortFunction", testInitializationFromUnsortedDictionaryAndSortFunction),
|
||||
("testInitializationFromValuesAndKeyPath", testInitializationFromValuesAndKeyPath),
|
||||
("testInitializationFromValuesAndKeyProviderClosure", testInitializationFromValuesAndKeyProviderClosure),
|
||||
]
|
||||
}
|
||||
|
||||
extension InsertionsTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__InsertionsTests = [
|
||||
("testIndexBasedInsertion_duplicateKey", testIndexBasedInsertion_duplicateKey),
|
||||
("testIndexBasedInsertion_invalidIndex", testIndexBasedInsertion_invalidIndex),
|
||||
("testIndexBasedInsertion_uniqueKey_endIndex", testIndexBasedInsertion_uniqueKey_endIndex),
|
||||
("testIndexBasedInsertion_uniqueKey_middleIndex", testIndexBasedInsertion_uniqueKey_middleIndex),
|
||||
("testIndexBasedInsertion_uniqueKey_startIndex", testIndexBasedInsertion_uniqueKey_startIndex),
|
||||
("testKeyBasedInsertion", testKeyBasedInsertion),
|
||||
]
|
||||
}
|
||||
|
||||
extension MapFilterTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__MapFilterTests = [
|
||||
("testCompactMapValues", testCompactMapValues),
|
||||
("testFilter", testFilter),
|
||||
("testMapValues", testMapValues),
|
||||
]
|
||||
}
|
||||
|
||||
extension RemovalTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__RemovalTests = [
|
||||
("testIndexBasedRemoval_viaMethod_invalidIndex", testIndexBasedRemoval_viaMethod_invalidIndex),
|
||||
("testIndexBasedRemoval_viaMethod_validIndex", testIndexBasedRemoval_viaMethod_validIndex),
|
||||
("testKeyBasedRemoval_viaMethod_existingKey", testKeyBasedRemoval_viaMethod_existingKey),
|
||||
("testKeyBasedRemoval_viaMethod_invalidKey", testKeyBasedRemoval_viaMethod_invalidKey),
|
||||
("testKeyBasedRemoval_viaSubscript_existingKey", testKeyBasedRemoval_viaSubscript_existingKey),
|
||||
("testKeyBasedRemoval_viaSubscript_invalidKey", testKeyBasedRemoval_viaSubscript_invalidKey),
|
||||
("testPopFirstEmpty", testPopFirstEmpty),
|
||||
("testPopFirstNonEmpty", testPopFirstNonEmpty),
|
||||
("testPopLastEmpty", testPopLastEmpty),
|
||||
("testPopLastNonEmpty", testPopLastNonEmpty),
|
||||
("testRemoveAll", testRemoveAll),
|
||||
("testRemoveFirstNonEmpty", testRemoveFirstNonEmpty),
|
||||
("testRemoveLastNonEmpty", testRemoveLastNonEmpty),
|
||||
]
|
||||
}
|
||||
|
||||
extension ReorderingTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__ReorderingTests = [
|
||||
("testPartitioning", testPartitioning),
|
||||
("testPartitioning_throughSlice", testPartitioning_throughSlice),
|
||||
("testReversal", testReversal),
|
||||
("testReversal_throughSlice", testReversal_throughSlice),
|
||||
("testSwapAtDifferentIndices", testSwapAtDifferentIndices),
|
||||
("testSwapAtDifferentIndices_throughSlice", testSwapAtDifferentIndices_throughSlice),
|
||||
("testSwapAtSameIndex", testSwapAtSameIndex),
|
||||
("testSwapAtSameIndex_throughSlice", testSwapAtSameIndex_throughSlice),
|
||||
]
|
||||
}
|
||||
|
||||
extension SortingTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__SortingTests = [
|
||||
("testMutatingSort", testMutatingSort),
|
||||
("testMutatingSort_throughSlice", testMutatingSort_throughSlice),
|
||||
("testSortingUnsortedDictionary", testSortingUnsortedDictionary),
|
||||
("testSortingWithoutMutation", testSortingWithoutMutation),
|
||||
]
|
||||
}
|
||||
|
||||
extension SubscriptAmbiguityTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__SubscriptAmbiguityTests = [
|
||||
("testAccess", testAccess),
|
||||
("testIndexBasedUpdate", testIndexBasedUpdate),
|
||||
("testKeyBasedUpdate", testKeyBasedUpdate),
|
||||
]
|
||||
}
|
||||
|
||||
extension UpdatesTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__UpdatesTests = [
|
||||
("testIndexBasedUpdate_viaMethod_duplicateKey", testIndexBasedUpdate_viaMethod_duplicateKey),
|
||||
("testIndexBasedUpdate_viaMethod_newUniqueKey", testIndexBasedUpdate_viaMethod_newUniqueKey),
|
||||
("testIndexBasedUpdate_viaMethod_sameKey", testIndexBasedUpdate_viaMethod_sameKey),
|
||||
("testIndexBasedUpdate_viaSubscriptMultiple_mixedKeys", testIndexBasedUpdate_viaSubscriptMultiple_mixedKeys),
|
||||
("testIndexBasedUpdate_viaSubscriptMultiple_newUniqueKeys", testIndexBasedUpdate_viaSubscriptMultiple_newUniqueKeys),
|
||||
("testIndexBasedUpdate_viaSubscriptMultiple_sameKeys", testIndexBasedUpdate_viaSubscriptMultiple_sameKeys),
|
||||
("testIndexBasedUpdate_viaSubscriptSingle_newUniqueKey", testIndexBasedUpdate_viaSubscriptSingle_newUniqueKey),
|
||||
("testIndexBasedUpdate_viaSubscriptSingle_sameKey", testIndexBasedUpdate_viaSubscriptSingle_sameKey),
|
||||
("testKeyBasedUpdate", testKeyBasedUpdate),
|
||||
]
|
||||
}
|
||||
|
||||
public func __allTests() -> [XCTestCaseEntry] {
|
||||
return [
|
||||
testCase(AccessTests.__allTests__AccessTests),
|
||||
testCase(CapacityTests.__allTests__CapacityTests),
|
||||
testCase(CodingTests.__allTests__CodingTests),
|
||||
testCase(DescriptionTests.__allTests__DescriptionTests),
|
||||
testCase(InitializationTests.__allTests__InitializationTests),
|
||||
testCase(InsertionsTests.__allTests__InsertionsTests),
|
||||
testCase(MapFilterTests.__allTests__MapFilterTests),
|
||||
testCase(RemovalTests.__allTests__RemovalTests),
|
||||
testCase(ReorderingTests.__allTests__ReorderingTests),
|
||||
testCase(SortingTests.__allTests__SortingTests),
|
||||
testCase(SubscriptAmbiguityTests.__allTests__SubscriptAmbiguityTests),
|
||||
testCase(UpdatesTests.__allTests__UpdatesTests),
|
||||
]
|
||||
}
|
||||
#endif
|
||||
@@ -1,61 +0,0 @@
|
||||
#if !canImport(ObjectiveC)
|
||||
import XCTest
|
||||
|
||||
extension OrderedDictionaryTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__OrderedDictionaryTests = [
|
||||
("testAccessingContent", testAccessingContent),
|
||||
("testAccessingOrderedKeys", testAccessingOrderedKeys),
|
||||
("testAccessingOrderedValues", testAccessingOrderedValues),
|
||||
("testAccessingUnsortedDictionary", testAccessingUnsortedDictionary),
|
||||
("testCapacityGrowForElementInsertion", testCapacityGrowForElementInsertion),
|
||||
("testCapacityReservationViaInit", testCapacityReservationViaInit),
|
||||
("testCapacityReservationViaMethod", testCapacityReservationViaMethod),
|
||||
("testCompactMapValues", testCompactMapValues),
|
||||
("testCreatingIterator", testCreatingIterator),
|
||||
("testCreationFromDictionary", testCreationFromDictionary),
|
||||
("testDebugDescription", testDebugDescription),
|
||||
("testDescription", testDescription),
|
||||
("testEmptyDebugDescription", testEmptyDebugDescription),
|
||||
("testEmptyDescription", testEmptyDescription),
|
||||
("testEncodingAndDecodingViaJSON", testEncodingAndDecodingViaJSON),
|
||||
("testEncodingAndDecodingViaPropertyList", testEncodingAndDecodingViaPropertyList),
|
||||
("testIndexBasedInsertionsWithUniqueKeys", testIndexBasedInsertionsWithUniqueKeys),
|
||||
("testIndexBasedInsertionWithDuplicateKey", testIndexBasedInsertionWithDuplicateKey),
|
||||
("testIndexBasedInsertionWithIndexOutOfBounds", testIndexBasedInsertionWithIndexOutOfBounds),
|
||||
("testIndexBasedInsertionWithNegativeIndex", testIndexBasedInsertionWithNegativeIndex),
|
||||
("testIndexBasedRemoval", testIndexBasedRemoval),
|
||||
("testIndexBasedUpdateMethodByDuplicatingKey", testIndexBasedUpdateMethodByDuplicatingKey),
|
||||
("testIndexBasedUpdateMethodByReplacingSameKey", testIndexBasedUpdateMethodByReplacingSameKey),
|
||||
("testIndexBasedUpdateMethodWithNewUniqueKey", testIndexBasedUpdateMethodWithNewUniqueKey),
|
||||
("testInitializationUsingArrayLiteral", testInitializationUsingArrayLiteral),
|
||||
("testInitializationUsingDictionaryLiteral", testInitializationUsingDictionaryLiteral),
|
||||
("testInitializationUsingUnsortedDictionaryAndSortFunction", testInitializationUsingUnsortedDictionaryAndSortFunction),
|
||||
("testInitializationUsingValuesAndKeyProviderClosure", testInitializationUsingValuesAndKeyProviderClosure),
|
||||
("testInitializationUsingValuesAnyKeyPath", testInitializationUsingValuesAnyKeyPath),
|
||||
("testKeyBasedModifications", testKeyBasedModifications),
|
||||
("testKeyBasedRemoval", testKeyBasedRemoval),
|
||||
("testMapValues", testMapValues),
|
||||
("testMovingElements", testMovingElements),
|
||||
("testPopFirstEmpty", testPopFirstEmpty),
|
||||
("testPopFirstNonEmpty", testPopFirstNonEmpty),
|
||||
("testPopLastEmpty", testPopLastEmpty),
|
||||
("testPopLastNonEmpty", testPopLastNonEmpty),
|
||||
("testRemoveAll", testRemoveAll),
|
||||
("testRemoveFirstNonEmpty", testRemoveFirstNonEmpty),
|
||||
("testRemoveLastNonEmpty", testRemoveLastNonEmpty),
|
||||
("testRetrievingElementAtNonExistentIndex", testRetrievingElementAtNonExistentIndex),
|
||||
("testSliceAccess", testSliceAccess),
|
||||
("testSortingWithMutation", testSortingWithMutation),
|
||||
("testSortingWithoutMutation", testSortingWithoutMutation),
|
||||
]
|
||||
}
|
||||
|
||||
public func __allTests() -> [XCTestCaseEntry] {
|
||||
return [
|
||||
testCase(OrderedDictionaryTests.__allTests__OrderedDictionaryTests),
|
||||
]
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user