How to add new value to dictionary [[String : String]] - arrays

I have a dictionary, which looks like this:
var dict = [["number" : "1" ], ["number" : "2" ], ["number" : "3" ]]
Now I would like to add new value "level" : "(number of level)" to each index in my dictionary, and it should looks like that:
var dict = [["number" : "1", "level" : "one"], ["number" : "2", "level" : "two" ], ["number" : "3", "level" : "three" ]]
How can I add some value inside existing dictionary in this case?

What you have listed as a dictionary is actually an array of dictionaries. You can add an element to each of the directories by simply iterating the array.
You can use an NSNumberFormatter to convert the digits you have into equivalent words:
var anArray=[["number":"1"],["number":"2"],["number":"3"]]
let numberFormatter=NSNumberFormatter()
numberFormatter.numberStyle=NSNumberFormatterStyle.SpellOutStyle
for i in 0..<anArray.count {
if let numberString=anArray[i]["number"] {
if let number=Int(numberString) {
anArray[i]["level"]=numberFormatter.stringFromNumber(number)
}
}
}

As Paulw11 pointed out, you could use NSNumberFormatter to convert the digits to words:
let dictArray = [["number" : "1" ], ["number" : "2" ], ["number" : "3" ]]
let numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = NSNumberFormatterStyle.SpellOutStyle
let mappedDictArray = dictArray.map { var d = $0; d["level"] = numberFormatter.stringFromNumber(number); return d; }
However, if you're interested in using the level key only for UI purposes, you'd be better writing a Dictionary extension, as there's no point is storing a redundant value:
extension Dictionary {
func levelString() -> String {
let numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = NSNumberFormatterStyle.SpellOutStyle
return numberFormatter.stringFromNumber(self["number"] as? Int ?? 0)
}
}
which can be used like this:
dictArray[0].level()

Related

How to convert model with array to JSON array Swift 3

i have some question and solution, i have class model and it has array inside, my model like this
class WillModel{
var name: String = ""
var documents:[WillItems] = []
var isLFStatus : Bool = false
var isLFShared : Bool = false
}
class WillItems{
var documentPath: String = ""
var documentRemark: String = ""
}
i want to convert the result to JSON Array like
{
"name" : "value",
"documents" : [
{
"documentPath" : "value"
"documentRemark" : "value"
},
{
"documentPath" : "value2"
"documentRemark" : "value2"
}
],
"isLFStatus" : true,
"isLFShared" : true
}
i'm using Swift 3, thanks for your solutions
You can write a simple method to manually encode your model to json as below,
func encodeWillModel(_ model: WillModel) -> [String: Any] {
var params: [String: Any] = [:]
params["name"] = model.name
params["documents"] = model.documents.map({["documentPath": $0.documentPath,
"documentRemark": $0.documentRemark]})
params["isLFStatus"] = model.isLFStatus
params["isLFShared"] = model.isLFShared
return params
}
and you can pass any model to get the json something like,
let willModelJSON = encodeWillModel(yourModel)
But a better approach would be to use any json parsing library(SwiftyJSON, ObjectMapper) that supports Swift 3 or upgrade to Swift 4 and use Encodable/Decodable

Adding additional element to an existing JSON array

I have a case like, I want to add elements into a JSON array in TypeScript like below
[
{
"a" : "a1",
"b" : "b1"
}
]
Now I want to add values to the above object without creating new block, for example, I want to add key, value pair as "C": "c1" and "d": "d1". After doing this, my JSON array must look like below
[
{
"a" : "a1",
"b" : "b1"
"c" : "c1",
"d" : "d1"
}
]
What I tried:
let someData : any [] = [];
someData.push({
"a" : "a1",
"b" : b1"
})
someData.push({
"c" : "c1",
"d" : d1"
})
But it is creating two records like below, but this is wrong
[
{
"a" : "a1",
"b" : "b1"
}
{
"c" : "c1",
"d" : "d1"
}
]
as well I tried using unshift as below
someData.unshift({
"c" : "c1",
"d" : d1"
})
this is returning result object as
[
{
"a" : "a1",
"b" : "b1"
}
{
"c" : "c1",
"d" : "d1"
}
]
Is there any way to do?
For example,
for(int i =0; i<3; i++){
someData.push({
i : i+1
})
But the above block of code is creating wrong array structure, but inturn I want as below
{
0 :1,
1:2,
2:3
}
}
Its supposed to be like this...
let someData : any [] = [];
someData.push({
"a" : "a1",
"b" : b1"
})
someData[0]["c"] = "c1";
someData[0]["d"] = "d1";
So when you log the values of someData ... it will show
console.log(someData); //[{"a":"a1","b" : "b1", "c":"c1", "d":"d1"}]
for looping through values...
let valuesToPut = [["c","c1"],["d","d1"]];
for(let i = 0; i < valuesToPut.length; i++){
someData[0][valuesToPut[i][0]] = valuesToPut[i][1]
}
is little confusion between Object and Array, here you try to add some item to the Object who are store on index 0 of your array.
let try following code :
let someData : any [] = [];
// Create first index of array and create on it your Object.
someData.push({
"a" : "a1",
"b" : b1"
});
// Override first index of array by merge of previous data + new one.
someData[0] = Object.assign(someData[0], {
"c1" : "c1",
"d1" : "d1"
});
Object assign documentation
Another way to do this: Object.defineProperties()
As you mensioned, it is an array json format.
So, if you access some element in the array, you should indicate the array index.
ex:
let tempArray = [
{
"a" : "a1",
"b" : "b1"
}
]
=>
tempArray[0] has this value.
{
"a" : "a1",
"b" : "b1"
}
So, if you add some additional values to the tempArray[0], you should access the element like below :
tempArray[0]['c'] = "c1";
tempArray[0]['d'] = "d1";
//Let's start with what you're doing
let someData = [];
someData.push({
"a" : "a1",
"b" : "b1"
});
// pushes a collection of 2 key value pairs in as the first entry to someData
someData.push({
"c" : "c1",
"d" : "d1"
});
// pushes a collection of 2 key value pairs in as the second entry to someData
console.log(someData);
// what you might want to do:
someData = [];
someData.push({
"a" : "a1",
"b" : "b1"
});
// pushes a collection of 2 key value pairs in as the first entry to someData
someData[0].c = "c1";
//Sets a value to the key c in the first element of some data
someData[0].d = "d1";
//Sets a value to the key d in the first element of some data
console.log(someData)
// What you probably want to do.
someData = {};
for(let i of [1,2,3,4]){
someData[String.fromCharCode(i+96)] = String.fromCharCode(i+96)+"1";
}
console.log(someData)

How to recreate an array of dictionaries that belong to an alphabet letter?

I am trying to create a new array of objects from an existing array with objects within Swift 3. So I have an array of letters that contain objects that begin with that letter (the name key of that object) so that I can use that new array of dictionaries for the sections within a tableview. What I would like to have is that the first letters of each name key of each object will be examine, than extract the first letter of the name and store it as an array.
For example, a dictionary contains a name like "Lisa". The first letter is L and that one doesn't exists yet. It will create an array for the letter L. Check the next entry. If the next entry starts with an L again, add it to the array with the letter L. If the next one starts with an S, create a new array of S and add it to that array etc etc.
var names = [
[
"name" : "Lisa",
"street" : "Lorem ipsum"
],
[
"name" : "Jeffrey",
"street" : "Lorem ipsum"
],
[
"name" : "Arnold",
"street" : "Lorem ipsum"
],
[
"name" : "Jacob",
"street" : "Lorem ipsum"
],
[
"name" : "Sam",
"street" : "Lorem ipsum"
],
[
"name" : "Anita",
"street" : "Lorem ipsum"
],
[
"name" : "Lotte",
"street" : "Lorem ipsum"
]
]
func createArrarDic(array: [[String: String]]) -> [[String: String]] {
var result = [[String: String]]()
// Loop through array
for object in array {
for (key, value) in object {
let index = value.index(value.startIndex, offsetBy: 1)
let firstLetter = value.substring(to: index).uppercased()
print(firstLetter)
if result[firstLetter] != nil {
result[firstLetter]!.append(object)
} else {
result[firstLetter] = [object]
}
}
}
return result
}
createArrarDic(array: names)
I came up with above code, but it will throw me an error of:
Cannot subscript a value of type '[[String : String]]' with an index of type 'String'
It clear that I do something wrong with the result declaration, but can't figure out how it should be. The check for the first letter should be done in the object, but to reorder and create a new array for each letter it should not be done within that object, but one level above it.
Also the problem is that for(key, value) will examine all the key/value- pairs in that object and I only need the "name" key.
Update
I have came to the following code. I have the return type and the return value to [String: [String: String]]() because of it should start with the letter that contains the object referred to it.
The only problem now is that I don't know how to append an object to the array of a specific letter, because of .append or something like += operator doesn't work. Any thoughts on this one?
func createArrarDic(array: [[String: String]]) -> [String: [String: String]] {
var result = [String: [String: String]]()
// Loop through array
for object in array {
// Loop through dictionaries
for (_, value) in object {
let index = value.index(value.startIndex, offsetBy: 1)
let firstLetter = value.substring(to: index).uppercased()
print(firstLetter)
if result[firstLetter] != nil {
// Append
result[firstLetter]! += object
} else {
result[firstLetter] = object
}
}
}
return result
}
let changedArray = createArrarDic(array: names)
I think this does what you want:
func createArrarDic(array: [[String: String]]) -> [String: [[String: String]]] {
return array.reduce([String: [[String: String]]]()) { result, object in
guard let name = object["name"] else {
return result
}
if name.characters.count == 0 {
return result
}
let firstLetter = String(name[name.startIndex]).uppercased()
var mutableResult = result
if mutableResult[firstLetter] == nil {
mutableResult[firstLetter] = [object]
} else {
mutableResult[firstLetter]?.append(object)
}
return mutableResult
}
}

Appending to Array in Deeply Nested Dictionary of Arrays

I am having the following problem in Swift. First, I declare a data structure, like this:
var books = [String : Dictionary<String, Dictionary<String, Dictionary<String, Array<Dictionary<String, String>>>>>]()
I later initialize the var like this:
books = [
"Fiction" : [
"Genre Fiction" : [
"Mystery" : [
"Classics" : [
["Title" : "Ten Little Indians",
"Author" : "Agatha Christie",
"read" : "no"],
["Title" : "A Study in Scarlet",
"Author" : "Arthur Conan Doyle",
"read" : "no"],
]
]
]
]
]
Note that the compiler does not complain about it. Later, I create a new dictionary which I would like to append to that innermost array, like this:
var bookDict = Dictionary<String, String>()
bookDict = ["title" : dict.valueForKey("title") as! String,
"author": dict.valueForKey("author") as! String,
"read" : dict.valueForKey("read") as! String ]
books["category"]["genre"]["focus"]["set"].append(bookDict)
However, I get a compiler error that "Cannot invoke append with an argument list of type (Dictionary < String, String>)". This is confusing to me because the books data structure is declared such that that innermost array is an array of Dictionary< String, String>.
What am I missing?
It's not clear during compile time if the key is really present in the dictionary so dictionaries always return optional values. You have to check this optionals:
if let category = books["Fiction"] {
if let genre = category["Genre Fiction"] {
if let focus = genre["Mystery"] {
if var set = focus["Classics"] {
set.append(bookDict)
}
}
}
}
Or you can do it also like this:
books["Fiction"]?["Genre Fiction"]?["Mystery"]?["Classics"]?.append(bookDict)

Transform from dictionary to array in swift without a for loop

I have some data returned from the server that look like this:
let returnedFromServer = ["title" : ["abc", "def", "ghi"],
"time" : ["1234", "5678", "0123"],
"content":["qwerty", "asdfg", "zxcvb"]]
I want to transform it into something like this:
let afterTransformation =
[["title" : "abc",
"time" : "1234",
"content": "qwerty"],
["title" : "def",
"time" : "5678",
"content": "asdfg"],
["title" : "ghi",
"time" : "0123",
"content": "zxcvb"]]
My current implementation is as follows:
var outputArray = [[String : AnyObject]]()
for i in 0..<(returnedFromServer["time"] as [String]).count {
var singleDict = [String: AnyObject]()
for attribute in returnedFromServer {
singleDict[attribute] = returnedFromServer[attribute]?[i]
}
outputArray.append(singleDict)
}
This works fine but I think it is not a very elegant solution. Given that Swift has some neat features such as reduce, filter and map, I wonder if I can do the same job without explicitly using a loop.
Thanks for any help!
Using the ideas and the dictionary extension
extension Dictionary {
init(_ pairs: [Element]) {
self.init()
for (k, v) in pairs {
self[k] = v
}
}
func map<OutKey: Hashable, OutValue>(transform: Element -> (OutKey, OutValue)) -> [OutKey: OutValue] {
return Dictionary<OutKey, OutValue>(Swift.map(self, transform))
}
}
from
What's the cleanest way of applying map() to a dictionary in Swift?,
you could achieve this with
let count = returnedFromServer["time"]!.count
let outputArray = (0 ..< count).map {
idx -> [String: AnyObject] in
return returnedFromServer.map {
(key, value) in
return (key, value[idx])
}
}
Martin R’s answer is a good one and you should use that and accept his answer :-), but as an alternative to think about:
In an ideal world the Swift standard library would have:
the ability to initialize a Dictionary from an array of 2-tuples
a Zip3 in addition to a Zip2 (i.e. take 3 sequences and join them into a sequence of 3-tuples
an implementation of zipWith (i.e. similar to Zip3 but instead of just combining them into pairs, run a function on the given tuples to combine them together).
If you had all that, you could write the following:
let pairs = map(returnedFromServer) { (key,value) in map(value) { (key, $0) } }
assert(pairs.count == 3)
let inverted = zipWith(pairs[0],pairs[1],pairs[2]) { [$0] + [$1] + [$2] }
let arrayOfDicts = inverted.map { Dictionary($0) }
This would have the benefit of being robust to ragged input – it would only generate those elements up to the shortest list in the input (unlike a solution that takes a count from one specific list of the input). The downside it its hard-coded to a size of 3 but that could be fixed with a more general version of zipWith that took a sequence of sequences (though if you really wanted your keys to be strings and values to be AnyObjects not strings you’d have to get fancier.
Those functions aren’t all that hard to write yourself – though clearly way too much effort to write for this one-off case they are useful in multiple situations. If you’re interested I’ve put a full implementation in this gist.
I'd create 2 helpers:
ZipArray (similar to Zip2, but works with arbitrary length):
struct ZipArray<S:SequenceType>:SequenceType {
let _sequences:[S]
init<SS:SequenceType where SS.Generator.Element == S>(_ base:SS) {
_sequences = Array(base)
}
func generate() -> ZipArrayGenerator<S.Generator> {
return ZipArrayGenerator(map(_sequences, { $0.generate()}))
}
}
struct ZipArrayGenerator<G:GeneratorType>:GeneratorType {
var generators:[G]
init(_ base:[G]) {
generators = base
}
mutating func next() -> [G.Element]? {
var row:[G.Element] = []
row.reserveCapacity(generators.count)
for i in 0 ..< generators.count {
if let e = generators[i].next() {
row.append(e)
}
else {
return nil
}
}
return row
}
}
Basically, ZipArray flip the axis of "Array of Array", like:
[
["abc", "def", "ghi"],
["1234", "5678", "0123"],
["qwerty", "asdfg", "zxcvb"]
]
to:
[
["abc", "1234", "qwerty"],
["def", "5678", "asdgf"],
["ghi", "0123", "zxcvb"]
]
Dictionary extension:
extension Dictionary {
init<S:SequenceType where S.Generator.Element == Element>(_ pairs:S) {
self.init()
var g = pairs.generate()
while let (k:Key, v:Value) = g.next() {
self[k] = v
}
}
}
Then you can:
let returnedFromServer = [
"title" : ["abc", "def", "ghi"],
"time" : ["1234", "5678", "0123"],
"content":["qwerty", "asdfg", "zxcvb"]
]
let outputArray = map(ZipArray(returnedFromServer.values)) {
Dictionary(Zip2(returnedFromServer.keys, $0))
}

Resources