Golang and yaml with dynamic schema - parsing

I have a YAML structure with dynamic schema e.g. I can have the following yaml:
array:
- name: myvar
val: 1
- name: mymap
val: [ 1, 2]
Goyaml maps yaml to Go struct, which should declare definite type. Here, val is either a signle number, or an array or even a map.
Which is the best solution for this situation?

I decided to add an answer showing a type assertion instead of the reflect package. You can decide which is best for your application. I personally prefer the builtin functions over the complexity of the reflect package.
var data = `
array:
- name: myvar
val: 1
- name: mymap
val: [1, 2]
`
type Data struct {
Array []struct {
Name string
Val interface{}
}
}
func main() {
d := Data{}
err := yaml.Unmarshal([]byte(data), &d)
if err != nil {
log.Fatal(err)
}
for i := range d.Array {
switch val := d.Array[i].(type) {
case int:
fmt.Println(val) // is integer
case []int:
fmt.Println(val) // is []int
case []string:
fmt.Println(val) // is []string
// .... you get the idea
default:
log.Fatalf("Type unaccounted for: %+v\n", d.Array[i])
}
}
}

Related

Swift - Converting a Dictionary to a KeyValuePair [duplicate]

In Swift Charts the signature for chartForegroundStyleScale to set the ShapeStyle for each data series is:
func chartForegroundStyleScale<DataValue, S>(_ mapping: KeyValuePairs<DataValue, S>) -> some View where DataValue : Plottable, S : ShapeStyle
The KeyValuePairs initialiser (init(dictionaryLiteral: (Key, Value)...)) only takes a variadic parameter so any attempt to initialise a foreground style from an array (in my case <String, Color>) results in the error:
Cannot pass array of type '[(String, Color)]' as variadic arguments of type '(String, Color)'
In my application the names of the chart series are set dynamically from the data so although I can generate a [String : Color] dictionary or an array of (String, Color) tuples I can't see that it's possible to pass either of these into chartForegroundStyleScale? Unless I'm missing something this seems like a odd limitation in Swift charts that the series names need to be hard coded for this modifier?
OK I've found an approach that works as long as an arbitrary limitation to the number of entries is acceptable (example below with max size of 4:
func keyValuePairs<S, T>(_ from: [(S, T)]) -> KeyValuePairs<S, T> {
switch from.count {
case 1: return [ from[0].0 : from[0].1 ]
case 2: return [ from[0].0 : from[0].1, from[1].0 : from[1].1 ]
case 3: return [ from[0].0 : from[0].1, from[1].0 : from[1].1, from[2].0 : from[2].1 ]
default: return [ from[0].0 : from[0].1, from[1].0 : from[1].1, from[2].0 : from[2].1, from[3].0 : from[3].1 ]
}
In my case I know that there won't be more than 20 mappings so this func can just be extended to accommodate that number.
Not ideal, but it works...
You could also pass an array of colors to .chartForegroundStyleScale(range:). As long as you add the colors to the array in the same order you add your graph marks it should work fine.
Not incredibly elegant either, but this approach works with an arbitrary number or entries.
struct GraphItem: Identifiable {
var id = UUID()
var label: String
var value: Double
var color: Color
}
struct ContentView: View {
let data = [
GraphItem(label: "Apples", value: 2, color: .red),
GraphItem(label: "Pears", value: 3, color: .yellow),
GraphItem(label: "Melons", value: 5, color: .green)
]
var body: some View {
Chart {
ForEach(data, id: \.label) { item in
BarMark(
x: .value("Count", item.value),
y: .value("Fruit", item.label)
)
.foregroundStyle(by: .value("Fruit", item.label))
}
}
.chartForegroundStyleScale(range: graphColors(for: data))
}
func graphColors(for input: [GraphItem]) -> [Color] {
var returnColors = [Color]()
for item in input {
returnColors.append(item.color)
}
return returnColors
}
}

How to use Swift's higher order functions for parsing dynamic dictionary data in swift?

I am trying to parse the following json and want to retrieve the "key" of a dictionary whose value matches with the given value.
{ "OuterArrayHolder" :
[
{
"dictDynamicKey" : ["dynamicValue1", "dynamicValue2", "dynamicValue3"]
},
{
"dictAnotherDynamicKey" : ["dynamicValue4", "dynamicValue5", "dynamicValue6"]
},
]
}
[Note: Here in above json, all the keys and values are dynamic except "OuterArrayHolder".]
I have implemented it in a non-Swifty way and currently getting the expected output, but I am not getting how to accomplish the same behaviour using swift's higher-order functions.
Input : "dynamicValue2"
Expected output : "dictDynamicKey"
Current solution:
let inputValue = "dynamicValue2"
if !outerArrayHolder.isEmpty {
for dynamicDict in outerArrayHolder {
for (key, value) in dynamicDict {
if value.empty || !value.contains(inputValue) {
continue
} else {
//here if inputValue matches in contianed array (value is array in dictionary) then I want to use its "repective key" for further businisess logic.
}
}
}
}
I want to reduce these two for loops and want to use higher-order functions to achieve the exact behavior, Any help in this regard is really appreciated.
Can we convert your algorithm to a functional style? Yes. Is it a good idea? Probably not in this case. But here's how.
You didn't give any type information, so I'll use this type:
let outerArrayHolder: [[String: Any]] = [
[
"dictDynamicKey": ["dynamicValue1", "dynamicValue2", "dynamicValue3"]
],
[
"dictAnotherDynamicKey" : ["dynamicValue4", "dynamicValue5", "dynamicValue6"]
],
]
And you want to find the key corresponding to the array that contains inputValue:
let inputValue = "dynamicValue2"
The functional strategy is to map each dictionary in outerArrayHolder to its first key that has a matching value. If a dictionary has no such key, the dictionary is mapped to nil. Then we throw away the nils and take the first remaining value.
We can do it with filter, as requested:
let key = outerArrayHolder.lazy
.compactMap {
$0.lazy
.filter { ($0.value as? [String])?.contains(inputValue) ?? false }
.map { $0.key }
.first }
.first
But we can save a lazy and a first using first(where:):
let key = outerArrayHolder.lazy
.compactMap({
$0
.first(where: { ($0.value as? [String])?.contains(inputValue) ?? false })
.map { $0.key }
}).first
I don't see what this has to do with higher-order functions. If the outer key is known, I would simply write
// just building your structure
let d1 = ["dictDynamicKey" : ["dynamicValue1", "dynamicValue2", "dynamicValue3"]]
let d2 = ["dictAnotherDynamicKey" : ["dynamicValue4", "dynamicValue5", "dynamicValue6"]]
let d = ["OuterArrayHolder" : [d1, d2]]
// this is the actual code:
func find(_ target:String) -> String? {
for dict in d["OuterArrayHolder"]! {
for (k,v) in dict {
if v.contains(target) {return k}
}
}
return nil
}
That's just what you're doing, only it's clean.
There's no higher order function that does precisely what you're looking for. The closest is first(where:), but the problem is that the result is just Bool, and you don't have a way to cleanly fish out data related to the found case.
You could write something like:
extension Sequence {
func findFirst<T>(where predicate: (Element) throws -> T?) rethrows -> T? {
for element in self {
if let result = try predicate(element) {
return result
}
}
return nil
}
}
and then use it like:
let dictionaries = [
[
"dictDynamicKey" : ["dynamicValue1", "dynamicValue2", "dynamicValue3"]
],
[
"dictAnotherDynamicKey" : ["dynamicValue4", "dynamicValue5", "dynamicValue6"]
],
]
let desiredValue = "dynamicValue2"
extension Sequence {
func findFirst<T>(where predicate: (Element) throws -> T?) rethrows -> T? {
for element in self {
if let result = try predicate(element) {
return result
}
}
return nil
}
}
let result = dictionaries.findFirst(where: { dict in
dict.findFirst(where: { key, values in
values.contains(desiredValue) ? key : nil
})
})
print(result as Any) // => Optional("dictDynamicKey")
But it's probably more complexity than it's probably worth. I would recommend Matt's solution.
Scaled solution
You haven't clarified on this, but I suspect that you probably need to do this a bunch of times. In that case, linear searching through this gets really slow. By searching for keys by their values, you're not taking advantage of the key benefit of dictionaries: constant-time access to a value by its key. Your code is:
Linear-searching through the array of dictionaries, introduces an O(dictionaries.count) factor
For each dict in the array in #1, linear-searching through the key/value pairs, which introduces a O(dict.count) factor
For each key/value pair in the dict in #2, linear-searching through array of values, which introduces a O(valueArray.count) factor.
The total time complexity multiplies up to O(dictionaries.count * averageDict.count * averageValueArray.count), which gets really slow really quick.
Instead, you can spend some compute cost up-front, to create a new data structure that is better able to service the kinds of queries you want to run on it. In this case, you can "invert" a dictionary.
extension Dictionary {
func inverted<T>() -> [T: Key] where Dictionary.Value == [T] {
let invertedKeyValuePairs = self
.lazy
.flatMap { oldKey, oldValues in
oldValues.map { oldValue in (key: oldValue, value: oldKey) as (T, Key) }
}
return Dictionary<T, Key>(uniqueKeysWithValues: invertedKeyValuePairs)
}
}
// Example usage:
let valuesByKeys = [
"a": [1, 2, 3],
"b": [4, 5, 6]
]
let keysPerValue = valuesByKeys.inverted()
keysPerValue.forEach { key, value in print("key: \(key), value: \(value)") }
// Which results in:
// key: 3, value: a
// key: 4, value: b
// key: 5, value: b
// key: 1, value: a
// key: 6, value: b
// key: 2, value: a
Given such an inverted implementation, you can invert each dict of your input set, and merge them all together:
let invertedDictionary = Dictionary(uniqueKeysWithValues: dictionaries.flatMap { $0.inverted() })
invertedDictionary.forEach { key, value in print("key: \(key), value: \(value)") }
// Result:
key: dynamicValue6, value: dictAnotherDynamicKey
key: dynamicValue1, value: dictDynamicKey
key: dynamicValue2, value: dictDynamicKey
key: dynamicValue3, value: dictDynamicKey
key: dynamicValue4, value: dictAnotherDynamicKey
key: dynamicValue5, value: dictAnotherDynamicKey
You can store and share this dictionary, which can give constant time (O(1)) access to the key that was associated with any desired value:
print(invertedDictionary[desiredValue] as Any) // => Optional("dictDynamicKey")

Have a variable with multiple types in Swift

I would like to have a variable, which can have multiple types (only ones, I defined), like:
var example: String, Int = 0
example = "hi"
This variable should be able to hold only values of type Int and String.
Is this possible?
Thanks for your help ;)
An “enumeration with associated value” might be what you are looking for:
enum StringOrInt {
case string(String)
case int(Int)
}
You can either assign a string or an integer:
var value: StringOrInt
value = .string("Hello")
// ...
value = .int(123)
Retrieving the contents is done with a switch-statement:
switch value {
case .string(let s): print("String:", s)
case .int(let n): print("Int:", n)
}
If you declare conformance to the Equatable protocol then
you can also check values for equality:
enum StringOrInt: Equatable {
case string(String)
case int(Int)
}
let v = StringOrInt.string("Hi")
let w = StringOrInt.int(0)
if v == w { ... }
Here is how you can achieve it. Works exactly how you'd expect.
protocol StringOrInt { }
extension Int: StringOrInt { }
extension String: StringOrInt { }
var a: StringOrInt = "10"
a = 10 //> 10
a = "q" //> "q"
a = 0.8 //> Error
NB! I would not suggest you to use it in production code. It might be confusing for your teammates.
UPD: as #Martin R mentioned: Note that this restricts the possible types only “by convention.” Any module (or source file) can add a extension MyType: StringOrInt { } conformance.
No, this is not possible for classes, structs, etc.
But it is possible for protocols.
You can this:
protocol Walker {
func go()
}
protocol Sleeper {
func sleep()
}
var ab = Walker & Sleeper
or even
struct Person {
var name: String
}
var ab = Person & Walker & Sleeper
But I don't recomment use this way.
More useful this:
struct Person: Walker, Sleeper {
/// code
}
var ab = Person
You can use Tuple.
Example:
let example: (String, Int) = ("hi", 0)
And access each data by index:
let stringFromExampleTuple = example.0 // "hi"
let intFromtExampleTuple = example.1 // 0

Description of Struct not printed in console when executed from Unit Test

I have defined a Struct that adheres to the Printable protocol so I can inspect its value.
struct GridPosition : Hashable, Printable
{
var row: Int
var col: Int
var hashValue: Int {
get {
return ((row + col) * (row + col + 1)) / 2 + col
}
}
var description: String {
return "row: \(row) col: \(col)"
}
}
func == (el: GridPosition, er: GridPosition) -> Bool {
return el.hashValue == er.hashValue
}
When I print the value inside my ViewController the description shows up as expected in the console.
var pos = GridPosition(row: 1, col: 2)
println("value: \(pos)")
Console output:
value: row: 1 col: 2
However, when I try to print the value from my unit test I only get its specifier:
value: GridTests.GridPosition
How can I make my unit test console output more helpful? I'm using Xcode 6.2 (6C131e)
Try explicitly referencing the struct's description property.
println("value: \(pos.description)")
I believe there are other posts on SO where people note that this is a bug, at least in a playground, and explicitly referencing description was the recommended workaround. I haven't seen the bug rear its head in a unit test, but that may be what is going on.

Counting number of Arrays that contain the same two values

Given a Dictionary<String, Arrary<Int>> find the how many entries have the same two specified values in the first 5 entries in the Array<Int>.
For example:
Given:
let numberSeries = [
"20022016": [07,14,36,47,50,02,05],
"13022016": [16,07,32,36,41,07,09],
"27022016": [14,18,19,31,36,04,05],
]
And the values: 7 and 36, the result should be 2 since the first and second entry have both the values 7 and 36 in the first 5 entries of the entry's array.
I've tried to accomplish this many ways, but I haven't been able to get it to work.
This is my current attempt:
//created a dictionary with (key, values)
let numberSeries = [
"20022016": [07,14,36,47,50,02,05],
"13022016": [16,07,32,36,41,07,09],
"27022016": [14,18,19,31,36,04,05],
]
var a = 07 //number to look for
var b = 36 // number to look for
// SearchForPairAB // search for pair // Doesn't Work.
var ab = [a,b] // pair to look for
var abPairApearedCount = 0
for (kind, numbers) in numberSeries {
for number in numbers[0...4] {
if number == ab { //err: Cannot invoke '==' with argument listof type Int, #Value [Int]
abPairApearedCount++
}
}
}
This gives the error: Cannot invoke '==' with argument listof type Int, #Value [Int] on the line: if number == ab
You can't use == to compare an Int and Array<Int>, that just doesn't make any sense from a comparison perspective. There are lots of different ways you can achieve what you're trying to do though. In this case I'd probably use map/reduce to count your pairs.
The idea is to map the values in your ab array to Bool values determined by whether or not the value is in your numbers array. Then, reduce those mapped Bools to a single value: true if they're all true, or false. If that reduced value is true, then we found the pair so we increment the count.
var ab = [a,b] // pair to look for
var abPairApearedCount = 0
for (kind, numbers) in numberSeries {
let found = ab.map({ number in
// find is a built-in function that returns the index of the value
// in the array, or nil if it's not found
return find(numbers[0...4], number) != nil
}).reduce(true) { (result, value: Bool) in
return result && value
}
if found {
abPairApearedCount++
}
}
That can actually be compacted quite a bit by using some of Swift's more concise syntax:
var ab = [a,b] // pair to look for
var abPairApearedCount = 0
for (kind, numbers) in numberSeries {
let found = ab.map({ find(numbers[0...4], $0) != nil }).reduce(true) { $0 && $1 }
if found {
abPairApearedCount++
}
}
And, just for fun, can be compacted even further by using reduce instead of a for-in loop:
var ab = [a,b] // pair to look for
var abPairApearedCount = reduce(numberSeries, 0) { result, series in
result + (ab.map({ find(series.1[0...4], $0) != nil }).reduce(true) { $0 && $1 } ? 1 : 0)
}
That's getting fairly unreadable though, so I'd probably expand some of that back out.
So here's my FP solution, aimed at decomposing the problem into easily digestible and reusable bite-sized chunks:
First, we define a functor that trims an array to a given length:
func trimLength<T>(length: Int) -> ([T]) -> [T] {
return { return Array($0[0...length]) }
}
Using this we can trim all the elements using map(array, trimLength(5))
Now, we need an predicate to determine if all the elements of one array are in the target array:
func containsAll<T:Equatable>(check:[T]) -> ([T]) -> Bool {
return { target in
return reduce(check, true, { acc, elem in return acc && contains(target, elem) })
}
}
This is the ugliest bit of code here, but essentially it's just iterating over check and insuring that each element is in the target array. Once we've got this we can use filter(array, containsAll([7, 26])) to eliminate all elements of the array that don't contain all of our target values.
At this point, we can glue the whole thing together as:
filter(map(numberSeries.values, trimLength(5)), containsAll([7, 36])).count
But long lines of nested functions are hard to read, let's define a couple of helper functions and a custom operator:
func rmap<S:SequenceType, T>(transform:(S.Generator.Element)->T) -> (S) -> [T] {
return { return map($0, transform) }
}
func rfilter<S:SequenceType>(predicate:(S.Generator.Element)->Bool) -> (S) -> [S.Generator.Element] {
return { sequence in return filter(sequence, predicate) }
}
infix operator <^> { associativity left }
func <^> <S, T>(left:S, right:(S)->T) -> T {
return right(left)
}
And a convenience function to count it's inputs:
func count<T>(array:[T]) -> Int {
return array.count
}
Now we can condense the whole thing as:
numberSeries.values <^> rmap(trimLength(5)) <^> rfilter(containsAll([7, 36])) <^> count

Resources