Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f08ff3e872 | |||
| f0da8a43be | |||
| dbd6b816db | |||
| 9818933b59 | |||
| 88cc1ad101 | |||
| 4cd37bd207 | |||
| 5e62daeff2 | |||
| 52e42cd938 | |||
| dbf4484048 | |||
| 804f2847b4 | |||
| 46952da3c3 | |||
| acb62df760 | |||
| f303e02f63 | |||
| 244d0128c9 | |||
| 0d5e16fc4f | |||
| ecd3f176e5 | |||
| ac3f8fa5eb | |||
| 19046fb4a8 | |||
| 005ef5c3ee | |||
| 68f2557694 | |||
| 72d4779e9b | |||
| 8547583792 | |||
| 3688348461 | |||
| f875439709 | |||
| 1920f07525 | |||
| 69ed90dcfc | |||
| 33fa9622ce | |||
| 468ad1c83a | |||
| 174c21d58f | |||
| 5f076fccbb | |||
| 642b5623b7 | |||
| 03b5817530 | |||
| 7ba07fcef1 | |||
| 0e1273e2ab | |||
| 7b8b3c39a3 | |||
| 1e5c2926a7 | |||
| b678702e3d | |||
| 4e4b205dcb | |||
| b345a3e8df | |||
| 2cf9220874 | |||
| 08e9ff5371 | |||
| e7adba0cb9 |
@@ -0,0 +1,42 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
macos:
|
||||
name: macOS (Xcode ${{ matrix.xcode }})
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
# See https://github.com/actions/virtual-environments/tree/master/images/macos
|
||||
# No Xcode with support for Swift 4.2 available
|
||||
xcode:
|
||||
- "11.5_beta" # Swift 5.2.4
|
||||
- "11.4" # Swift 5.2
|
||||
- "11" # Swift 5.1
|
||||
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
|
||||
|
||||
linux:
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: swift:5.2
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Build
|
||||
run: swift build -v
|
||||
- name: Test
|
||||
run: swift test -v
|
||||
+8
-29
@@ -1,32 +1,11 @@
|
||||
# OS X
|
||||
# SPM
|
||||
.build
|
||||
.swiftpm
|
||||
|
||||
# Xcode
|
||||
xcuserdata/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Ruby
|
||||
.ruby-*
|
||||
.rbenv-*
|
||||
|
||||
# Xcode
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
*.hmap
|
||||
*.ipa
|
||||
*.xcuserstate
|
||||
*.xctimeline
|
||||
|
||||
# Carthage
|
||||
Carthage
|
||||
|
||||
# SPM
|
||||
.build
|
||||
|
||||
+7
-27
@@ -1,49 +1,29 @@
|
||||
# Git
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
# OS
|
||||
os: osx
|
||||
language: objective-c
|
||||
language: swift
|
||||
|
||||
# Xcode Project
|
||||
xcode_project: OrderedDictionary.xcodeproj
|
||||
|
||||
# Build Matrix
|
||||
matrix:
|
||||
include:
|
||||
# Swift 4.0 / macOS
|
||||
- osx_image: xcode9.2
|
||||
xcode_scheme: OrderedDictionary-Mac
|
||||
# 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"
|
||||
# Swift 4.0 / iOS
|
||||
- osx_image: xcode9.2
|
||||
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"
|
||||
# Swift 4.1 / macOS
|
||||
- osx_image: xcode9.3beta
|
||||
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"
|
||||
# Swift 4.1 / iOS
|
||||
- osx_image: xcode9.3beta
|
||||
# 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"
|
||||
|
||||
# Build Script
|
||||
script:
|
||||
- Scripts/build.sh
|
||||
- .travis/build.sh
|
||||
|
||||
@@ -32,6 +32,9 @@ if [[ -z $XCODE_DESTINATION ]]; then
|
||||
fi
|
||||
|
||||
set -o pipefail
|
||||
|
||||
swift --version
|
||||
|
||||
xcodebuild $XCODE_ACTION \
|
||||
-project "$TRAVIS_XCODE_PROJECT" \
|
||||
-scheme "$TRAVIS_XCODE_SCHEME" \
|
||||
@@ -50,5 +53,5 @@ fi
|
||||
|
||||
if [[ $XCODE_SDK = "macosx" ]]; then
|
||||
echo "SDK is ${XCODE_SDK}, validating playground..."
|
||||
. Scripts/validate-playgrounds.sh
|
||||
. .travis/validate-playgrounds.sh
|
||||
fi
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2019 Lukas Kubanek
|
||||
Copyright © 2015-2020 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
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import XCTest
|
||||
|
||||
import OrderedDictionaryTests
|
||||
|
||||
var tests = [XCTestCaseEntry]()
|
||||
tests += OrderedDictionaryTests.__allTests()
|
||||
|
||||
XCTMain(tests)
|
||||
@@ -7,6 +7,8 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
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 */; };
|
||||
@@ -38,9 +40,9 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
8024947E2277123600AB44C7 /* Package@swift-4.2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Package@swift-4.2.swift"; sourceTree = "<group>"; };
|
||||
8024947F2277136D00AB44C7 /* Package@swift-5.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Package@swift-5.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>"; };
|
||||
@@ -108,7 +110,6 @@
|
||||
80DE329420F4DD910053EDA7 /* LICENSE.md */,
|
||||
80DE329220F4CAFA0053EDA7 /* Package.swift */,
|
||||
8024947E2277123600AB44C7 /* Package@swift-4.2.swift */,
|
||||
8024947F2277136D00AB44C7 /* Package@swift-5.swift */,
|
||||
8055B0521E201D24009DC3EE /* Sources */,
|
||||
80B28EB11E201F72007E3A77 /* Playgrounds */,
|
||||
8055B0571E201DF3009DC3EE /* Tests */,
|
||||
@@ -135,6 +136,7 @@
|
||||
80E8E21C1E20301E00395E49 /* OrderedDictionary.swift */,
|
||||
80A203A01F3F483700622481 /* OrderedDictionary+Codable.swift */,
|
||||
80E8E22F1E2133D100395E49 /* OrderedDictionary+Description.swift */,
|
||||
8048C8AA22D8911B0086B88B /* OrderedDictionary+Deprecated.swift */,
|
||||
);
|
||||
path = Sources;
|
||||
sourceTree = "<group>";
|
||||
@@ -346,6 +348,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8048C8AB22D8911B0086B88B /* OrderedDictionary+Deprecated.swift in Sources */,
|
||||
80E8E2301E2133D100395E49 /* OrderedDictionary+Description.swift in Sources */,
|
||||
80E8E21D1E20301E00395E49 /* OrderedDictionary.swift in Sources */,
|
||||
80A203A11F3F483700622481 /* OrderedDictionary+Codable.swift in Sources */,
|
||||
@@ -364,6 +367,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8048C8AC22D8911B0086B88B /* OrderedDictionary+Deprecated.swift in Sources */,
|
||||
80E8E2311E2133D100395E49 /* OrderedDictionary+Description.swift in Sources */,
|
||||
80E8E21F1E20425B00395E49 /* OrderedDictionary.swift in Sources */,
|
||||
80A203A21F3F4C1F00622481 /* OrderedDictionary+Codable.swift in Sources */,
|
||||
@@ -532,6 +536,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lukaskubanek.OrderedDictionary;
|
||||
PRODUCT_NAME = OrderedDictionary;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 4.2;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -553,6 +558,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lukaskubanek.OrderedDictionary;
|
||||
PRODUCT_NAME = OrderedDictionary;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 4.2;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -598,6 +604,7 @@
|
||||
PRODUCT_NAME = OrderedDictionary;
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 4.2;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -620,6 +627,7 @@
|
||||
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 = "1020"
|
||||
LastUpgradeVersion = "1140"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -27,6 +27,15 @@
|
||||
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>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
@@ -39,17 +48,6 @@
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8055B0371E201C5D009DC3EE"
|
||||
BuildableName = "OrderedDictionary.framework"
|
||||
BlueprintName = "OrderedDictionary-Mac"
|
||||
ReferencedContainer = "container:OrderedDictionary.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@@ -70,8 +68,6 @@
|
||||
ReferencedContainer = "container:OrderedDictionary.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1020"
|
||||
LastUpgradeVersion = "1140"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -27,6 +27,15 @@
|
||||
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">
|
||||
@@ -39,17 +48,6 @@
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "80B28E961E201EC8007E3A77"
|
||||
BuildableName = "OrderedDictionary.framework"
|
||||
BlueprintName = "OrderedDictionary-iOS"
|
||||
ReferencedContainer = "container:OrderedDictionary.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@@ -70,8 +68,6 @@
|
||||
ReferencedContainer = "container:OrderedDictionary.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
+2
-3
@@ -1,4 +1,4 @@
|
||||
// swift-tools-version:4
|
||||
// swift-tools-version:5.0
|
||||
|
||||
import PackageDescription
|
||||
|
||||
@@ -22,6 +22,5 @@ let package = Package(
|
||||
dependencies: ["OrderedDictionary"],
|
||||
path: "Tests"
|
||||
)
|
||||
],
|
||||
swiftLanguageVersions: [4]
|
||||
]
|
||||
)
|
||||
|
||||
@@ -23,5 +23,5 @@ let package = Package(
|
||||
path: "Tests"
|
||||
)
|
||||
],
|
||||
swiftLanguageVersions: [.v4, .v4_2]
|
||||
swiftLanguageVersions: [.v4_2]
|
||||
)
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
// swift-tools-version:5.0
|
||||
|
||||
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: [.v5]
|
||||
)
|
||||
@@ -31,19 +31,3 @@ let plistDecoder = PropertyListDecoder()
|
||||
let orderedDictionary3 = try! plistDecoder.decode(OrderedDictionary<String, Int>.self, from: plistData)
|
||||
|
||||
orderedDictionary1 == orderedDictionary3
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Non-codable Type
|
||||
// ======================================================= //
|
||||
|
||||
struct NonCodableType {
|
||||
var string: String
|
||||
}
|
||||
|
||||
let orderedDictionary4: OrderedDictionary<String, NonCodableType> = [
|
||||
"A" : NonCodableType(string: "Foo"),
|
||||
"B" : NonCodableType(string: "Bar"),
|
||||
"C" : NonCodableType(string: "Baz")
|
||||
]
|
||||
|
||||
//try? jsonEncoder.encode(x)
|
||||
|
||||
@@ -1,64 +1,77 @@
|
||||
# OrderedDictionary
|
||||
|
||||
[](https://travis-ci.org/lukaskubanek/OrderedDictionary) [](https://github.com/lukaskubanek/OrderedDictionary/releases) [](https://developer.apple.com/swift/ "Swift 4")  [](https://github.com/Carthage/Carthage)
|
||||
[](https://github.com/Carthage/Carthage) [](LICENSE.md)
|
||||
<p align="left">
|
||||
<a href="https://github.com/lukaskubanek/OrderedDictionary/releases">
|
||||
<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+">
|
||||
</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">
|
||||
</a>
|
||||
<a href="https://github.com/Carthage/Carthage">
|
||||
<img src="https://img.shields.io/badge/Carthage-compatible-brightgreen.svg?style=flat-square" alt="Carthage">
|
||||
</a>
|
||||
<a href="LICENSE.md">
|
||||
<img src="https://img.shields.io/badge/license-MIT-lightgrey.svg?style=flat-square" alt="License: MIT">
|
||||
</a>
|
||||
<a href="https://twitter.com/lukaskubanek">
|
||||
<img src="https://img.shields.io/badge/contact-@lukaskubanek-olive.svg?style=flat-square" alt="Twitter: @lukaskubanek">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
**OrderedDictionary** is a lightweight implementation of an ordered dictionary data structure in Swift.
|
||||
OrderedDictionary is a lightweight implementation of an ordered dictionary data structure in Swift.
|
||||
|
||||
The `OrderedDictionary` struct is a generic collection which combines the features of `Dictionary` and `Array` 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` 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.
|
||||
|
||||
`OrderedDictionary` provides similar APIs like collections from the Swift standard library. This includes accessing contents by keys or indices, inserting and removing elements, iterating, sorting etc.
|
||||
`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.
|
||||
|
||||
Internally, `OrderedDictionary` uses a backing store composed of an instance of `Dictionary` for storing the key-value pairs and an instance of `Array` for managing the ordered keys. This means it is not the most performant implementation possible, but it gets its job done by reusing most functionality from the Swift standard library.
|
||||
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.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Swift 4.0, 4.2, 5.0
|
||||
- Xcode 9.2+
|
||||
- Swift 4.2+
|
||||
- Xcode 10.0+
|
||||
- iOS 8.0+ / macOS 10.10+
|
||||
|
||||
*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.*
|
||||
|
||||
## Installation
|
||||
|
||||
The library is distributed as a Swift framework and can be integrated into your project in following ways:
|
||||
### Swift Package Manager
|
||||
|
||||
#### Carthage
|
||||
To install OrderedDictionary using the [Swift Package Manager](https://swift.org/package-manager/), add it as a dependency into your `Package.swift` file:
|
||||
|
||||
If you use [Carthage](https://github.com/Carthage/Carthage) for managing your dependencies, put OrderedDictionary into your `Cartfile`:
|
||||
|
||||
```plain
|
||||
github "lukaskubanek/OrderedDictionary"
|
||||
```swift
|
||||
let package = Package(
|
||||
...
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/lukaskubanek/OrderedDictionary.git", from: "3.0.0")
|
||||
],
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
Then, drag either the `OrderedDictionary.xcodeproj` or the `OrderedDictionary.framework` into your project/workspace and link your target against the `OrderedDictionary.framework`. Also make sure that the framework [gets copied](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application) to your application bundle.
|
||||
### Carthage
|
||||
|
||||
#### Swift Package Manager
|
||||
|
||||
Another option is to use the [Swift Package Manager](https://swift.org/package-manager/). If you prefer this option, put OrderedDictionary as a dependency to your `Package.swift`:
|
||||
To install OrderedDictionary using [Carthage](https://github.com/Carthage/Carthage), add it as a dependency into your `Cartfile`:
|
||||
|
||||
```plain
|
||||
.package(url: "https://github.com/lukaskubanek/OrderedDictionary.git", from: "2.2.0")
|
||||
github "lukaskubanek/LoremSwiftum"
|
||||
```
|
||||
|
||||
#### Git Submodules
|
||||
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.
|
||||
|
||||
Yet another option is using [Git submodules](http://git-scm.com/book/en/v2/Git-Tools-Submodules) and integrating the Xcode project `OrderedDictionary.xcodeproj` from the submodule directly to your Xcode workspace.
|
||||
### Git Submodules
|
||||
|
||||
#### ⚠️ Note About CocoaPods
|
||||
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.
|
||||
|
||||
Although there is high demand for [CocoaPods](https://cocoapods.org) support, this method won't be officially supported by this library. Since I'm not using CocoaPods myself and since I think this method will be once replaced by the Swift Package Manager, I don't want to put any effort in maintaining an official podspec. If you really want to include this library via CocoaPods, there is still a way by creating a custom podspec (see the last section of [this post](https://guides.cocoapods.org/syntax/podfile.html#pod)).
|
||||
### ⚠️ 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).
|
||||
|
||||
## Changelog
|
||||
|
||||
The changelog is managed on the [GitHub releases page](https://github.com/lukaskubanek/OrderedDictionary/releases).
|
||||
|
||||
## Author
|
||||
|
||||
Lukas Kubanek // [lukaskubanek.com](http://lukaskubanek.com) // [@kubanekl](https://twitter.com/kubanekl)
|
||||
|
||||
## License
|
||||
|
||||
**OrderedDictionary** is provided under the [MIT License](LICENSE.md).
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#if swift(>=4.1)
|
||||
|
||||
extension OrderedDictionary: Encodable where Key: Encodable, Value: Encodable {
|
||||
|
||||
/// __inheritdoc__
|
||||
@@ -34,91 +32,6 @@ extension OrderedDictionary: Decodable where Key: Decodable, Value: Decodable {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
extension OrderedDictionary: Encodable {
|
||||
|
||||
/// __inheritdoc__
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
// Since Swift 4.0 lacks the protocol conditional conformance support, we have to make the
|
||||
// whole OrderedDictionary type conform to Encodable and assert that the key and value
|
||||
// types conform to Encodable. Furthermore, we leverage a trick of super encoders to be
|
||||
// able to encode objects without knowing their exact types. This trick was used in the
|
||||
// standard library for encoding/decoding Dictionary before Swift 4.1.
|
||||
|
||||
_assertTypeIsEncodable(Key.self, in: type(of: self))
|
||||
_assertTypeIsEncodable(Value.self, in: type(of: self))
|
||||
|
||||
var container = encoder.unkeyedContainer()
|
||||
|
||||
for (key, value) in self {
|
||||
let keyEncoder = container.superEncoder()
|
||||
try (key as! Encodable).encode(to: keyEncoder)
|
||||
|
||||
let valueEncoder = container.superEncoder()
|
||||
try (value as! Encodable).encode(to: valueEncoder)
|
||||
}
|
||||
}
|
||||
|
||||
private func _assertTypeIsEncodable<T>(_ type: T.Type, in wrappingType: Any.Type) {
|
||||
guard T.self is Encodable.Type else {
|
||||
if T.self == Encodable.self || T.self == Codable.self {
|
||||
preconditionFailure("\(wrappingType) does not conform to Encodable because Encodable does not conform to itself. You must use a concrete type to encode or decode.")
|
||||
} else {
|
||||
preconditionFailure("\(wrappingType) does not conform to Encodable because \(T.self) does not conform to Encodable.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension OrderedDictionary: Decodable {
|
||||
|
||||
/// __inheritdoc__
|
||||
public init(from decoder: Decoder) throws {
|
||||
// Since Swift 4.0 lacks the protocol conditional conformance support, we have to make the
|
||||
// whole OrderedDictionary type conform to Decodable and assert that the key and value
|
||||
// types conform to Decodable. Furthermore, we leverage a trick of super decoders to be
|
||||
// able to decode objects without knowing their exact types. This trick was used in the
|
||||
// standard library for encoding/decoding Dictionary before Swift 4.1.
|
||||
|
||||
self.init()
|
||||
|
||||
_assertTypeIsDecodable(Key.self, in: type(of: self))
|
||||
_assertTypeIsDecodable(Value.self, in: type(of: self))
|
||||
|
||||
var container = try decoder.unkeyedContainer()
|
||||
|
||||
let keyMetaType = (Key.self as! Decodable.Type)
|
||||
let valueMetaType = (Value.self as! Decodable.Type)
|
||||
|
||||
while !container.isAtEnd {
|
||||
let keyDecoder = try container.superDecoder()
|
||||
let key = try keyMetaType.init(from: keyDecoder) as! Key
|
||||
|
||||
guard !container.isAtEnd else { throw DecodingError.unkeyedContainerReachedEndBeforeValue(decoder.codingPath) }
|
||||
|
||||
let valueDecoder = try container.superDecoder()
|
||||
let value = try valueMetaType.init(from: valueDecoder) as! Value
|
||||
|
||||
self[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
private func _assertTypeIsDecodable<T>(_ type: T.Type, in wrappingType: Any.Type) {
|
||||
guard T.self is Decodable.Type else {
|
||||
if T.self == Decodable.self || T.self == Codable.self {
|
||||
preconditionFailure("\(wrappingType) does not conform to Decodable because Decodable does not conform to itself. You must use a concrete type to encode or decode.")
|
||||
} else {
|
||||
preconditionFailure("\(wrappingType) does not conform to Decodable because \(T.self) does not conform to Decodable.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
extension DecodingError {
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
+136
-77
@@ -27,32 +27,20 @@ public struct OrderedDictionary<Key: Hashable, Value>: BidirectionalCollection {
|
||||
// MARK: - Initialization
|
||||
// ======================================================= //
|
||||
|
||||
/// Creates an empty ordered dictionary.
|
||||
public init() {}
|
||||
|
||||
/// Creates an ordered dictionary from a sequence of values keyed by a key which gets extracted
|
||||
/// from the value in the provided closure.
|
||||
///
|
||||
/// - Parameter values: The sequence of values.
|
||||
/// - Parameter getKey: The closure which provides a key for the given value from the values
|
||||
/// sequence.
|
||||
public init<Values: Sequence>(
|
||||
values: Values,
|
||||
keyedBy getKey: (Value) -> Key
|
||||
) where Values.Element == Value {
|
||||
self.init(values.map { (getKey($0), $0) })
|
||||
/// Initializes an empty ordered dictionary.
|
||||
public init() {
|
||||
self._orderedKeys = [Key]()
|
||||
self._keysToValues = [Key: Value]()
|
||||
}
|
||||
|
||||
/// Creates an ordered dictionary from a sequence of values keyed by a key loaded from the value
|
||||
/// at the given key path.
|
||||
///
|
||||
/// - Parameter values: The sequence of values.
|
||||
/// - Parameter keyPath: The key path for the value to locate its key at.
|
||||
public init(values: [Value], keyedBy keyPath: KeyPath<Value, Key>) {
|
||||
self.init(values.map { ($0[keyPath: keyPath], $0) })
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// Creates an ordered dictionary from a regular unsorted dictionary by sorting it using the
|
||||
/// Initializes an ordered dictionary from a regular unsorted dictionary by sorting it using
|
||||
/// the given sort function.
|
||||
///
|
||||
/// - Parameter unsorted: The unsorted dictionary.
|
||||
@@ -61,20 +49,65 @@ public struct OrderedDictionary<Key: Hashable, Value>: BidirectionalCollection {
|
||||
unsorted: Dictionary<Key, Value>,
|
||||
areInIncreasingOrder: (Element, Element) throws -> Bool
|
||||
) rethrows {
|
||||
let elements = try unsorted
|
||||
.map { (key: $0.key, value: $0.value) }
|
||||
.sorted(by: areInIncreasingOrder)
|
||||
let keysAndValues = try Array(unsorted).sorted(by: areInIncreasingOrder)
|
||||
|
||||
self.init(elements)
|
||||
self.init(
|
||||
uniqueKeysWithValues: keysAndValues,
|
||||
minimumCapacity: unsorted.count
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates an ordered dictionary from a sequence of key-value pairs.
|
||||
/// Initializes an ordered dictionary from a sequence of values keyed by a unique key extracted
|
||||
/// from the value using the given closure.
|
||||
///
|
||||
/// - Parameter elements: The key-value pairs that will make up the new ordered dictionary.
|
||||
/// Each key in `elements` must be unique.
|
||||
public init<S: Sequence>(_ elements: S) where S.Element == Element {
|
||||
for (key, value) in elements {
|
||||
precondition(!containsKey(key), "Elements sequence contains duplicate keys")
|
||||
/// - 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
|
||||
}
|
||||
}
|
||||
@@ -288,7 +321,11 @@ public struct OrderedDictionary<Key: Hashable, Value>: BidirectionalCollection {
|
||||
/// - 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
|
||||
@@ -543,7 +580,7 @@ public struct OrderedDictionary<Key: Hashable, Value>: BidirectionalCollection {
|
||||
public func sorted(
|
||||
by areInIncreasingOrder: (Element, Element) throws -> Bool
|
||||
) rethrows -> OrderedDictionary<Key, Value> {
|
||||
return OrderedDictionary(try _sortedElements(by: areInIncreasingOrder))
|
||||
return OrderedDictionary(uniqueKeysWithValues: try _sortedElements(by: areInIncreasingOrder))
|
||||
}
|
||||
|
||||
private func _sortedElements(
|
||||
@@ -552,15 +589,73 @@ public struct OrderedDictionary<Key: Hashable, Value>: BidirectionalCollection {
|
||||
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]()
|
||||
fileprivate var _orderedKeys: [Key]
|
||||
|
||||
/// The backing storage for the mapping of keys to values.
|
||||
fileprivate var _keysToValues = [Key: Value]()
|
||||
fileprivate var _keysToValues: [Key: Value]
|
||||
|
||||
}
|
||||
|
||||
@@ -568,8 +663,6 @@ public struct OrderedDictionary<Key: Hashable, Value>: BidirectionalCollection {
|
||||
// MARK: - Subtypes
|
||||
// ======================================================= //
|
||||
|
||||
#if swift(>=4.1)
|
||||
|
||||
/// 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>>
|
||||
@@ -586,45 +679,26 @@ public typealias OrderedDictionaryKeys<Key: Hashable, Value> = LazyMapCollection
|
||||
/// the base ordered dictionary on-the-fly.
|
||||
public typealias OrderedDictionaryValues<Key: Hashable, Value> = LazyMapCollection<OrderedDictionary<Key, Value>, Value>
|
||||
|
||||
#else
|
||||
|
||||
/// A view into an ordered dictionary whose indices are a subrange of the indices of the ordered
|
||||
/// dictionary.
|
||||
public typealias OrderedDictionarySlice<Key: Hashable, Value> = BidirectionalSlice<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> = LazyMapBidirectionalCollection<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> = LazyMapBidirectionalCollection<OrderedDictionary<Key, Value>, Value>
|
||||
|
||||
#endif
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Literals
|
||||
// ======================================================= //
|
||||
|
||||
extension OrderedDictionary: ExpressibleByArrayLiteral {
|
||||
|
||||
/// Creates an ordered dictionary initialized from an array literal containing a list of
|
||||
/// key-value pairs.
|
||||
/// 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(elements)
|
||||
self.init(uniqueKeysWithValues: elements)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension OrderedDictionary: ExpressibleByDictionaryLiteral {
|
||||
|
||||
/// Creates an ordered dictionary initialized from a dictionary literal.
|
||||
/// Initializes an ordered dictionary initialized from a dictionary literal. Every key in
|
||||
/// `elements` must be unique.
|
||||
public init(dictionaryLiteral elements: (Key, Value)...) {
|
||||
self.init(elements.map { element in
|
||||
self.init(uniqueKeysWithValues: elements.map { element in
|
||||
let (key, value) = element
|
||||
return (key: key, value: value)
|
||||
})
|
||||
@@ -636,23 +710,8 @@ extension OrderedDictionary: ExpressibleByDictionaryLiteral {
|
||||
// MARK: - Equatable Conformance
|
||||
// ======================================================= //
|
||||
|
||||
#if swift(>=4.1)
|
||||
|
||||
extension OrderedDictionary: Equatable where Value: Equatable {}
|
||||
|
||||
#endif
|
||||
|
||||
extension OrderedDictionary where Value: Equatable {
|
||||
|
||||
/// Returns a Boolean value that indicates whether the two given ordered dictionaries with
|
||||
/// equatable values are equal.
|
||||
public static func == (lhs: OrderedDictionary, rhs: OrderedDictionary) -> Bool {
|
||||
return lhs._orderedKeys == rhs._orderedKeys
|
||||
&& lhs._keysToValues == rhs._keysToValues
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Dictionary Extension
|
||||
// ======================================================= //
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.2.0</string>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2015-2019 Lukas Kubanek. All rights reserved.</string>
|
||||
<string>Copyright © 2015-2020 Lukas Kubanek. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
|
||||
@@ -2,26 +2,6 @@ import OrderedDictionary
|
||||
import Foundation
|
||||
import XCTest
|
||||
|
||||
#if !swift(>=4.1)
|
||||
|
||||
/// This is a shim for testing the equality in Swift <4.1.
|
||||
public func XCTAssertEqual<K, V: Equatable>(
|
||||
_ expression1: OrderedDictionary<K, V>,
|
||||
_ expression2: OrderedDictionary<K, V>,
|
||||
_ message: @autoclosure () -> String = "",
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line
|
||||
) {
|
||||
return XCTAssertTrue(
|
||||
expression1 == expression2,
|
||||
message,
|
||||
file: file,
|
||||
line: line
|
||||
)
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
struct TestValue: Equatable {
|
||||
var string: String
|
||||
static func == (lhs: TestValue, rhs: TestValue) -> Bool {
|
||||
@@ -42,7 +22,7 @@ class OrderedDictionaryTests: XCTestCase {
|
||||
("C", 3)
|
||||
]
|
||||
|
||||
let expected = OrderedDictionary<String, Int>([
|
||||
let expected = OrderedDictionary<String, Int>(uniqueKeysWithValues: [
|
||||
(key: "A", value: 1),
|
||||
(key: "B", value: 2),
|
||||
(key: "C", value: 3)
|
||||
@@ -58,7 +38,7 @@ class OrderedDictionaryTests: XCTestCase {
|
||||
"C": 3
|
||||
]
|
||||
|
||||
let expected = OrderedDictionary<String, Int>([
|
||||
let expected = OrderedDictionary<String, Int>(uniqueKeysWithValues: [
|
||||
(key: "A", value: 1),
|
||||
(key: "B", value: 2),
|
||||
(key: "C", value: 3)
|
||||
@@ -70,10 +50,10 @@ class OrderedDictionaryTests: XCTestCase {
|
||||
func testInitializationUsingValuesAndKeyProviderClosure() {
|
||||
let actual = OrderedDictionary(
|
||||
values: [1, 2, 3],
|
||||
keyedBy: { "\($0)" }
|
||||
uniquelyKeyedBy: { "\($0)" }
|
||||
)
|
||||
|
||||
let expected = OrderedDictionary<String, Int>([
|
||||
let expected = OrderedDictionary<String, Int>(uniqueKeysWithValues: [
|
||||
(key: "1", value: 1),
|
||||
(key: "2", value: 2),
|
||||
(key: "3", value: 3)
|
||||
@@ -89,10 +69,10 @@ class OrderedDictionaryTests: XCTestCase {
|
||||
TestValue(string: "B"),
|
||||
TestValue(string: "C")
|
||||
],
|
||||
keyedBy: \.string
|
||||
uniquelyKeyedBy: \.string
|
||||
)
|
||||
|
||||
let expected = OrderedDictionary<String, TestValue>([
|
||||
let expected = OrderedDictionary<String, TestValue>(uniqueKeysWithValues: [
|
||||
(key: "A", value: TestValue(string: "A")),
|
||||
(key: "B", value: TestValue(string: "B")),
|
||||
(key: "C", value: TestValue(string: "C"))
|
||||
@@ -113,7 +93,7 @@ class OrderedDictionaryTests: XCTestCase {
|
||||
areInIncreasingOrder: { $0.key < $1.key }
|
||||
)
|
||||
|
||||
let expected = OrderedDictionary([
|
||||
let expected = OrderedDictionary(uniqueKeysWithValues: [
|
||||
(key: 1, value: "bar"),
|
||||
(key: 2, value: "foo"),
|
||||
(key: 3, value: "bam"),
|
||||
@@ -133,7 +113,7 @@ class OrderedDictionaryTests: XCTestCase {
|
||||
3: "bam"
|
||||
].sorted(by: { $0.key < $1.key })
|
||||
|
||||
let expected = OrderedDictionary([
|
||||
let expected = OrderedDictionary(uniqueKeysWithValues: [
|
||||
(key: 1, value: "bar"),
|
||||
(key: 2, value: "foo"),
|
||||
(key: 3, value: "bam"),
|
||||
@@ -511,6 +491,28 @@ class OrderedDictionaryTests: XCTestCase {
|
||||
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
|
||||
// ======================================================= //
|
||||
@@ -527,6 +529,46 @@ class OrderedDictionaryTests: XCTestCase {
|
||||
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
|
||||
// ======================================================= //
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
#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