How to sort a Dictionary in Swift - swift

How can I sort a dictionary in Swift?
My Dictionary declaration:
var colorDictionary = Dictionary<Pixel, Int>() //pixel class stores RGB values of a pixel, Int stores the appearing times of the same color showing on the same image.
My target:
I need the elements in the dictionary sorted by value (appearing times of colors) from high to low.
What I tried:
I have done some research online, and I know the Dictionary in swift doesn't provide the sort function. So I wrote the following code:
var tempArray = Array(colorDictionary.keys)
var sortedKeys: () = sort(&tempArray){
var obj1 = self.colorDictionary[$0]
var obj2 = self.colorDictionary[$1]
return obj1>obj2
}
println("the most color: \(colorDictionary[tempArray[0])")
Output I got: "the most color: Optional(27)" //where 27 is the highest appearing time of the color, which is the value of the dictionary.
Question: How could I make it return the key as well?
My Pixel Class:
//for making the hashvalue stuff equatable
func ==(lhs: Pixel, rhs: Pixel) -> Bool {
return lhs.hashValue == rhs.hashValue
}
//customized class for storing pixel info of an image
class Pixel: Hashable {
//RGB components from one pixel
let r: CGFloat
let g: CGFloat
let b: CGFloat
var y: CGFloat = 0
var u: CGFloat = 0
var v: CGFloat = 0
var theUIColorOfThisPixel:UIColor
//adding for make the key hashable
var hashValue: Int {
return theUIColorOfThisPixel.hashValue
}
init(_thePixel: UIColor){
r = _thePixel.components.red
g = _thePixel.components.green
b = _thePixel.components.blue
theUIColorOfThisPixel=UIColor(red: r, green: g, blue: b, alpha: 1)
rgbToYuv(r, _g: g, _b: b)
}
}
[Problem solved]
My solution:
if I convert the result to Int (e.g. Int(colorDictionary[tempArray[0]]), it will just return the appearing time of the most common color on the image. For getting the UIColor of the pixel, I used:
var theMostUIColor: UIColor = tempArray[0].theUIColorOfThisPixel
I thought after storing my dictionary to the Array, it will just store the values. But now I found it actually stores the keys as well. Thanks for all the people who replied on this post. I appreciate!

You can return an array of tuples. Where each tuple has two values; the Pixel and the integer.
See: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html#//apple_ref/doc/uid/TP40014097-CH10-ID164

Related

Mark closure element mutable Swift

I have 2 structs, first is:
struct LineData {
init (name: String,
colorValue: String,
values: [Int]){
self.name = name
self.colorValue = colorValue
self.values = values
}
private var cachedMaxValue: Int? = nil
let name: String
let colorValue: String
let values: [Int]
// describe max value for Y axis for specific Line
mutating func maxValue() -> Int{
if let cached = cachedMaxValue {
return cached
}
self.cachedMaxValue = values.max()
return cachedMaxValue ?? 0
}
}
Second have array of LineData structs:
struct CharData {
init(xAxis: XAxis,
lines: [LineData]){
self.xAxis = xAxis
self.lines = lines
}
private var cachedMaxValue: Int? = nil
var xAxis: XAxis
var lines: [LineData]
// describe max value for Y axis among lines
func maxValue() -> Int{
var maxValues: [Int] = []
lines.forEach{it in
maxValues.append(it.maxValue())
}
return 0
}
}
Code above not compile, because, of error on method maxValues for struct CharData. It says Cannot use mutating member on immutable value: 'it' is a 'let' constant
What i want is, iterate through an array of lines and among it max values find greater value.
Since lines is an ordinary array, how about simply:
for i in 0..<lines.count {
maxValues.append(lines[i].maxValue())
}
perhaps not quite as Swifty, but nothing gets copied. The optimizer ought to give you pretty much the same performance as forEach.
It's the it parameter/object in the forEach that's immutable. Just like the error says: "it is a let". You could probably do something like this:
lines.forEach { it in
var mutableIt = it
maxValues.append(mutableIt.maxValue())
}
It should be noted that this will create a mutable copy of the "it" struct instance.

RGB Values doing strange things when colorizing? - Swift

func colorBall() {
let colorize1 = SKAction.colorizeWithColor(UIColor.redColor(), colorBlendFactor: 1.0, duration: 0.1)
let colorize2 = SKAction.colorizeWithColor(UIColor.greenColor(), colorBlendFactor: 1.0, duration: 0.1)
let colorize3 = SKAction.colorizeWithColor(UIColor.blueColor(), colorBlendFactor: 1.0, duration: 0.1)
let actions = [colorize1, colorize2, colorize3]
let randomIndex = Int(arc4random_uniform(3))
self.Ball.runAction(actions[randomIndex])
}
var colorBucket = [UIColor]()
func randomColor() -> UIColor {
if colorBucket.isEmpty {
fillBucket()
}
let randomIndex = Int(arc4random_uniform(UInt32(colorBucket.count)))
let randomColor = colorBucket[randomIndex]
colorBucket.removeAtIndex(randomIndex)
return randomColor
}
func fillBucket() {
colorBucket = [UIColor.redColor(), UIColor.greenColor(), UIColor.blueColor()]
}
When I run this code in my game, and print out the color value of my ball, it sometimes prints out numbers like this:
UIDeviceRGBColorSpace 1 2.98023e-08 2.98023e-08 1
Why does it do this? I just want it to say: UIDeviceRGBColorSpace 0 0 1 1 if it's blue, IDeviceRGBColorSpace 1 0 0 1 if it's red, etc.
How can I keep those numbers from going higher than one, or much lower than one? What makes them do that in my code?
Based partially on zneak's answer I've made this (no thrills or frills) extension to UIColor which could come in handy:
extension UIColor {
func isVisuallyEqual(color: UIColor) -> Bool {
let compareValues = CGColorGetComponents(color.CGColor)
let values = CGColorGetComponents(self.CGColor)
let count = CGColorGetNumberOfComponents(self.CGColor)
if count != CGColorGetNumberOfComponents(color.CGColor) {
debugPrint("color-parameter has mismatching colorSpace")
return false
}
for index in 0..<count {
if !fuzzyFloatCompares(values[index], float2: compareValues[index]) {
return false
}
}
return true
}
private func fuzzyFloatCompares(float1: CGFloat, float2: CGFloat) -> Bool {
let difference = float1 - float2
return difference >= -1/256 && difference <= 1/256
}
}
2.98023e-08 is 0.0000000298023. If you look up the value 2.98023e-08 on Google or another search engine, you can find several examples of people getting that value because of rounding errors. Rounding errors occur because of how computers treat floating-point numbers.
It's probably a rounding error from the interpolation code that colorizeWithColor uses, and you get it instead of zero. For practical purposes, when talking about color components about to be displayed to an end user, I'd say that anything smaller than 1/256 can be considered to be zero.
You can test if two floating point numbers are "about equal" like this (typed on my phone, not really guaranteed to work):
func areAboutTheSame(a: Double, b: Double) -> Bool {
let difference = a-b
return difference < 1/256 && difference > -1/256
}

'(Int, Int)' is not identical to 'CGPoint'

I got error: '(Int, Int)' is not identical to 'CGPoint'
How to convert a (Int, Int) to CGPoint
let zigzag = [(100,100),
(100,150),(150,150),
(150,200)]
override func drawRect(rect: CGRect)
{
// Get the drawing context.
let context = UIGraphicsGetCurrentContext()
// Create the shape (a vertical line) in the context.
CGContextBeginPath(context)
//Error is here
CGContextAddLines(context, zigzag, zigzag.count)
// Configure the drawing environment.
CGContextSetStrokeColorWithColor(context,UIColor.redColor().CGColor)
// Request the system to draw.
CGContextStrokePath(context)
}
CGContextAddLines() expects an array of CGPoint. If you already have an
array of (Int, Int) tuples then you can convert it with
let points = zigzag.map { CGPoint(x: $0.0, y: $0.1) }
An alternate way to avoid the boilerplate code required to create instances of the same type is to make CGPoint implement the ArrayLiteralConvertible, making it initializable by assigning an array of CGFloat:
extension CGPoint : ArrayLiteralConvertible {
public init(arrayLiteral elements: CGFloat...) {
self.x = elements.count > 0 ? elements[0] : 0.0
self.y = elements.count > 1 ? elements[1] : 0.0
}
}
and then use it as follows:
let zigzag:[CGPoint] = [
[100,100],
[100,150],
[150,150],
[150,200]
]
A few notes:
stylistically, it doesn't look good - it would be good if literals could be used for tuples, but I am not aware of any way to do that
if an empty array is used, the CGPoint is initialized with x = 0 and y = 0
if an array with one element is used, it is initialized with y = 0
if more than 2 values are used, all the ones after the 2nd are ignored
If it tells you to use CGPoint, use it! Just (number,number) is a pair of ints.
let zigzag = [CGPointMake(100,100),
CGPointMake(100,150),CGPointMake(150,150),
CGPointMake(150,200)]
Yet another:
func CGPoints(points:(x:CGFloat, y:CGFloat)...) -> [CGPoint] {
return map(points) { CGPoint($0) }
}
let zigzag = CGPoints(
(100,100),(100,150),(150,150),(150,200)
)

Typecast UnsafeMutablePointer<Void> to UnsafeMutablePointer<#Struct type#>

I created a struct in Swift called RGB, simple enough:
struct PixelRGB {
var r: CUnsignedChar = 0
var g: CUnsignedChar = 0
var b: CUnsignedChar = 0
init(red: CUnsignedChar, green: CUnsignedChar, blue: CUnsignedChar) {
r = red
g = green
b = blue
}
}
And I have a pointer var imageData: UnsafeMutablePointer<PixelRGB>!.
I wish to malloc some space for this pointer, but malloc returns UnsafeMutablePointer<Void> and I cannot cast it like below:
imageData = malloc(UInt(dataLength)) as UnsafeMutablePointer<PixelRGB> // 'Void' is not identical to `PixelRGB`
Anyway to solve this? Thank you for your help!
I think what you want to say is something like this:
imageData = UnsafeMutablePointer<PixelRGB>.alloc(dataLength)
Have you tried the following?
imageData = unsafeBitCast(malloc(UInt(dataLength)), UnsafeMutablePointer<PixelRGB>.self)
Ref: Using Legacy C APIs with Swift

Create random CGPoint with Swift

So, I'm trying to develop a simple game written in Swift, but I'm having trouble doing a pretty simple thing. I can't manage to create a random CGPoint... When using arc4random, a compiler error shows up telling me that I can't use Int32 in a CGPoint. So, Is there any way to do this? Any workaround? Thanks!
can also maybe make use of Swift's extensions of base types to create a reusable set of overloaded functions of CGPoint. Maybe something like:
extension CGPoint {
func random()->CGPoint { return CGPoint(x:Int(arc4random()%1000),y:Int(arc4random()%1000))}
func random(range:Int)->CGPoint {
return CGPoint(x:Int(arc4random()%range),y:Int(arc4random()%range))}
func random(rangeX:Int, rangeY:Int)->CGPoint {
return CGPoint(x:Int(arc4random()%rangeX),y:Int(arc4random()%rangeY))}
}
You can then write random CGPoints like this:
var p = CGPoint.random()
//random x and y with a range of 1000
or
var p = CGPoint.random(range:100)
//random x and y with a range of 100
or
var p = CGPoint.random(rangeX:200, rangeY:400)
//random x up to 200 and random y with a range of up to 400
Granted, I'm not in the Xcode IDE at the moment to check syntax / if it compiles correctly but hope that could be of help :-)
...
//////////////////
Swift 1.2 Update
//////////////////
Seems these type-level function calls are not allowed anymore with extensions...at least for CGPoint; probably because CGPoint is actually a struct and not a class based on the current IOS documentation.
Here's a more in-depth version of my extension that allows for Range types.
This is confirmed working as of XCode 6.4 Beta
(Github repository with Playground file found here:
https://github.com/princetrunks/Random-CGPoint-Extension)
//creates random CGPoints in Swift as of XCode Beta 6.4 (6E7)
extension CGPoint {
/*private functions that help alleviate the ambiguity of the modulo bias
and nested typecasting as well as recycle similar functionality
for either Int or Range type parameter inputs */
private func randomInt(num:Int) ->Int{
return Int(arc4random_uniform(UInt32(num)))
}
private func randomIntFromRange(numRange:Range<Int>) ->Int{
return Int(arc4random_uniform(UInt32((numRange.endIndex - numRange.startIndex) + numRange.startIndex)))
}
//private variable for the default range
private var defaultRange : Int{
get{return 1000}
}
//(a) public variable that creates a default random CGPoint
static var randomPoint = CGPoint.zeroPoint.random()
//(b) default random point creation
func random()->CGPoint { return CGPoint(x:randomInt(defaultRange),y:randomInt(defaultRange))}
//(c) using an Int parameter for both the random x and y range
func random(range:Int)->CGPoint {
return CGPoint(x:randomInt(range),y:randomInt(range))
}
//(d) allows for the specification of the x and y random range
func random(#rangeX:Int, rangeY:Int)->CGPoint {
return CGPoint(x:randomInt(rangeX),y:randomInt(rangeY))
}
//(e) allows the same functionality as (c) but with a Range<Int> type parameter
func random(range:Range<Int>)->CGPoint {
return CGPoint(x:randomIntFromRange(range), y:randomIntFromRange(range))
}
//(f) allows the same functionality as (d) but with a Range<Int> type parameter
func random(#rangeX:Range<Int>, rangeY:Range<Int> )->CGPoint {
return CGPoint(x:randomIntFromRange(rangeX), y:randomIntFromRange(rangeY))
}
}
Here's how we can test this extension:
//(a)
let r = CGPoint.randomPoint
//(b)
var anotherRandomPoint = r.random()
//(c)
anotherRandomPoint = r.random(1000)
//(d)
anotherRandomPoint = r.random(0...1000)
//(e)
anotherRandomPoint = r.random(rangeX:90, rangeY: 2000)
//(f)
anotherRandomPoint = r.random(rangeX:0...90, rangeY: 0...2000)
// generates 100 random CGPoints between -1000 and 999
for _ in 0...100 {
anotherRandomPoint.random(-1000...1000)
}
hi what about constructing an Int? Int(arc4random())
e.g.
var p = CGPoint(x:Int(arc4random()%1000),y:Int(arc4random()%1000))
Swift 4,5
// Add some range
let minX = 0
let maxX = 100
let minY = 0
let maxY = 100
let randomX = CGFloat.random(in: minX..<maxX)
let randomY = CGFloat.random(in: minY..<maxY)
let random = CGPoint(x: randomX, y: randomY)
Here is an extension on CGPoint to generate random point based on your x,y closed range.
extension CGPoint {
static func randPoint(xRange: ClosedRange<CGFloat>, yRange: ClosedRange<CGFloat>) -> Self {
let x = CGFloat.random(in: xRange)
let y = CGFloat.random(in: yRange)
return .init(x: x, y: y)
}
}