Dictionary subtract issue for its similar array element - swift

I am trying to subtract two JSON/Dictionary. But the dictionary containing an array of objects is not working properly. Demo examples are given below. My intention is to prepare a new JSON file that property does not exist in the default JSON file. That is for the country-specific configuration I want to put into the new file.
[NOTE: If the value is different in any key of two dictionaries it should not be subtracted and we will consider the first dictionary value for the key. If the item is the same in any array then those similar array items should be removed. That is my main intention. I think it is clear now.]
let dict1 = [
"FALLBACK": "en-GB",
"SUPPORTED": [
"en-GB",
"fr",
"de",
"es",
"it",
"hu",
"nl",
"pl",
"sv",
"tr",
"ru",
"da",
"cs",
"fi",
"nb",
"pt",
"sr"
]
]
let dict2 = [
"FALLBACK": "fr",
"SUPPORTED": [
"en-GB",
"fr",
"de",
"es",
"it",
"hu",
"nl",
"pl",
"sv",
"tr",
"ru",
"da",
"cs",
"fi",
"nb",
"pt",
"sr",
"ar",
"bg",
"el",
"fa",
"hr",
"ka",
"lt",
"lv",
"ro",
"sk",
"sl",
"uk"
]
]
let diff = dict2.minus(dict: dict1)
Expected output (something like this):
[
"FALLBACK": "en-GB",
"SUPPORTED": [
"ar",
"bg",
"el",
"fa",
"hr",
"ka",
"lt",
"lv",
"ro",
"sk",
"sl",
"uk"
]
]
I followed this subtraction function. All are fine but the problem is during array subtraction.
extension Dictionary where Key: Comparable, Value: Equatable {
func minus(dict: [Key:Value]) -> [Key:Value] {
let entriesInSelfAndNotInDict = filter { dict[$0.0] != self[$0.0] }
return entriesInSelfAndNotInDict.reduce([Key:Value]()) { (res, entry) -> [Key:Value] in
var res = res
res[entry.0] = entry.1
return res
}
}
}

Related

Create JSON with class objects

I have almost ready what I want to do, however the method that converts to a JSON object does not help me to solve what is missing. I want to get the same thing, but there will be more content inside "add" and inside "firsts" and so I need them to be arrays of objects.
My code:
case class FirstIdentity(docType: String, docNumber: String, pId: String)
case class SecondIdentity(firm: String, code: String, orgType: String,
orgNumber: String, typee: String, perms: Seq[String])
case class General(id: Int, pName: String, description: String, add: Seq[SecondIdentity],
delete: Seq[String], act: String, firsts: Seq[FirstIdentity])
val someDF = Seq(
("0010XR_TYPE_6","0010XR", "222222", "6", "TYPE", "77444478", "6", 123, 1, "PF 1", "name", "description",
Seq("PERM1", "PERM2"))
).toDF("firm", "code", "org_number", "org_type", "type", "doc_number",
"doc_type", "id", "p_id", "p_name", "name", "description", "perms")
someDF.createOrReplaceTempView("vw_test")
val filter = spark.sql("""
select
firm, code, org_number, org_type, type, doc_number,
doc_type, id, p_id, p_name, name, description, perms
from vw_test
""")
val group =
filter.rdd.map(x => {
(
x.getInt(x.fieldIndex("id")),
x.getString(x.fieldIndex("p_name")),
x.getString(x.fieldIndex("description")),
SecondIdentity(
x.getString(x.fieldIndex("firm")),
x.getString(x.fieldIndex("code")),
x.getString(x.fieldIndex("org_type")),
x.getString(x.fieldIndex("org_number")),
x.getString(x.fieldIndex("type")),
x.getSeq(x.fieldIndex("perms"))
),
"act",
FirstIdentity(
x.getString(x.fieldIndex("doc_number")),
x.getString(x.fieldIndex("doc_type")),
x.getInt(x.fieldIndex("p_id")).toString
)
)
})
.toDF("id", "name", "desc", "add", "actKey", "firsts")
.groupBy("id", "name", "desc", "add", "actKey", "firsts")
.agg(collect_list("add").as("null"))
.drop("null")
group.toJSON.show(false)
result:
{
"id": 123,
"name": "PF 1",
"desc": "description",
"add": {
"firm": "0010XR_TYPE_6",
"code": "0010XR",
"orgType": "6",
"orgNumber": "222222",
"typee": "TYPE",
"perms": [
"PERM1",
"PERM2"
]
},
"actKey": "act",
"firsts": {
"docType": "77444478",
"docNumber": "6",
"pId": "1"
}
}
I want to have an array of "add" and also of "firsts"
this:
EDIT
{
"id": 123,
"name": "PF 1",
"desc": "description",
"add": [ <----
{
"firm": "0010XR_TYPE_6",
"code": "0010XR",
"orgType": "6",
"orgNumber": "222222",
"typee": "TYPE",
"perms": [
"PERM1",
"PERM2"
]
},
{
"firm": "0010XR_TYPE_6",
"code": "0010XR",
"orgType": "5",
"orgNumber": "11111",
"typee": "TYPE2",
"perms": [
"PERM1",
"PERM2"
]
}
],
"actKey": "act",
"firsts": [ <----
{
"docType": "77444478",
"docNumber": "6",
"pId": "1"
},
{
"docType": "411133",
"docNumber": "6",
"pId": "2"
}
]
}
As per your comment, you want to aggregate add depending on some grouping. Please check what all columns you want to group by. The columns which you want to Agrregate cannot be part of grouping. That will never work, and will give you always separate records.
This will work as per your expectations (I suppose):
val group =
filter.rdd.map(x => {
(
x.getInt(x.fieldIndex("id")),
x.getString(x.fieldIndex("p_name")),
x.getString(x.fieldIndex("description")),
SecondIdentity(
x.getString(x.fieldIndex("firm")),
x.getString(x.fieldIndex("code")),
x.getString(x.fieldIndex("org_type")),
x.getString(x.fieldIndex("org_number")),
x.getString(x.fieldIndex("type")),
x.getSeq(x.fieldIndex("perms"))
),
"act",
FirstIdentity(
x.getString(x.fieldIndex("doc_number")),
x.getString(x.fieldIndex("doc_type")),
x.getInt(x.fieldIndex("p_id")).toString
)
)
})
.toDF("id", "name", "desc", "add", "actKey", "firsts")
.groupBy("id", "name", "desc", "actKey")
.agg(collect_list("add").as("null"))
.drop("null")
Result:
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|value |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|{"id":123,"name":"PF 1","desc":"description","actKey":"act","collect_list(add)":[{"firm":"0010XR_TYPE_6","code":"0010XR","orgType":"6","orgNumber":"222222","typee":"TYPE","perms":["PERM1","PERM2"]},{"firm":"0010XR_TYPE_5","code":"0010XR","orgType":"5","orgNumber":"222223","typee":"TYPE","perms":["PERM1","PERM2"]}]}|
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
Inside your map function, you are not mapping the FirstEntity and SecondEntity as Seq hence the add is not getting converted to array.
Change your map function to this:
filter.rdd.map(x => {
(
x.getInt(x.fieldIndex("id")),
x.getString(x.fieldIndex("p_name")),
x.getString(x.fieldIndex("description")),
Seq(SecondIdentity(
x.getString(x.fieldIndex("firm")),
x.getString(x.fieldIndex("code")),
x.getString(x.fieldIndex("org_type")),
x.getString(x.fieldIndex("org_number")),
x.getString(x.fieldIndex("type")),
x.getSeq(x.fieldIndex("perms"))
)),
"act",
Seq(FirstIdentity(
x.getString(x.fieldIndex("doc_number")),
x.getString(x.fieldIndex("doc_type")),
x.getInt(x.fieldIndex("p_id")).toString
))
)
})
Will result into this:
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|value |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|{"id":123,"name":"PF 1","desc":"description","add":[{"firm":"0010XR_TYPE_6","code":"0010XR","orgType":"6","orgNumber":"222222","typee":"TYPE","perms":["PERM1","PERM2"]}],"actKey":"act","firsts":[{"docType":"77444478","docNumber":"6","pId":"1"}]}|
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

Updating Mongo DB collection field from object to array of objects

I had to change one of the fields of my collection in mongoDB from an object to array of objects containing a lot of data. New documents get inserted without any problem, but when attempted to get old data, it never maps to the original DTO correctly and runs into errors.
subject is the field that was changed in Students collection.
I was wondering is there any way to update all the records so they all have the same data type, without losing any data.
The old version of Student:
{
"_id": "5fb2ae251373a76ae58945df",
"isActive": true,
"details": {
"picture": "http://placehold.it/32x32",
"age": 17,
"eyeColor": "green",
"name": "Vasquez Sparks",
"gender": "male",
"email": "vasquezsparks#orbalix.com",
"phone": "+1 (962) 512-3196",
"address": "619 Emerald Street, Nutrioso, Georgia, 6576"
},
"subject":
{
"id": 0,
"name": "math",
"module": {
"name": "Advanced",
"semester": "second"
}
}
}
This needs to be updated to the new version like this:
{
"_id": "5fb2ae251373a76ae58945df",
"isActive": true,
"details": {
"picture": "http://placehold.it/32x32",
"age": 17,
"eyeColor": "green",
"name": "Vasquez Sparks",
"gender": "male",
"email": "vasquezsparks#orbalix.com",
"phone": "+1 (962) 512-3196",
"address": "619 Emerald Street, Nutrioso, Georgia, 6576"
},
"subject": [
{
"id": 0,
"name": "math",
"module": {
"name": "Advanced",
"semester": "second"
}
},
{
"id": 1,
"name": "history",
"module": {
"name": "Basic",
"semester": "first"
}
},
{
"id": 2,
"name": "English",
"module": {
"name": "Basic",
"semester": "second"
}
}
]
}
I understand there might be a way to rename old collection, create new and insert data based on old one in to new one. I was wondering for some direct way.
The goal is to turn subject into an array of 1 if it is not already an array, otherwise leave it alone. This will do the trick:
update args are (predicate, actions, options).
db.foo.update(
// Match only those docs where subject is an object (i.e. not turned into array):
{$expr: {$eq:[{$type:"$subject"},"object"]}},
// Actions: set subject to be an array containing $subject. You MUST use the pipeline version
// of the update actions to correctly substitute $subject in the expression!
[ {$set: {subject: ["$subject"] }} ],
// Do this for ALL matches, not just first:
{multi:true});
You can run this converter over and over because it will ignore converted docs.
If the goal is to convert and add some new subjects, preserving the first one, then we can set up the additional subjects and concatenate them into one array as follows:
var mmm = [ {id:8, name:"CORN"}, {id:9, name:"DOG"} ];
rc = db.foo.update({$expr: {$eq:[{$type:"$subject"},"object"]}},
[ {$set: {subject: {$concatArrays: [["$subject"], mmm]} }} ],
{multi:true});

How to match only documents where a condition, that includes another array, applies to all documents in an array?

There are a lot of questions/answers sounding exactly like what I'm looking for but I couldn't find a single one that actually worked for me.
Sample data:
{
"_id": "5daeb61790183fd4d4361d6c",
"orderMessageId": "7563_21",
"orderId": "OS00154",
"orderEntryDate": "2019-06-17T00:00:00.000Z",
"typeOfOrder": "ORD",
"express": false,
"name1": "xxx",
"name2": "xxx",
"name3": " ",
"contact": "IN KOMMISSION",
"street": "xxx",
"city": "xxx",
"zipcode": "1235",
"country": "xx",
"customerId": "51515",
"lnatMarketCode": "Corporate - Regulatory",
"shipmentCarrier": "ABH",
"typeOfShipment": "83",
"typeOfShipmentDescr": "xxx",
"orderTextfield": " ",
"orderTextfield02": " ",
"text2": " ",
"LinVw": [
{
"orderLineMessageId": "OS05451",
"orderLineId": 5,
"articleId": "19200",
"articleDescription": "xxx",
"productId": "OS1902",
"productDescription": "xxx",
"baseQuantityUnit": "EA",
"quantityOrdered": 2,
"isbn": "978357468",
"issn": " ",
"orderSubmissionDate": "2019-06-06T00:00:00.000Z",
"customerPurchaseOrderId": "728188175",
"lnatCustomerIdAtSupplier": " ",
"supplierDeliveryNoteId": " ",
"fulfillmentContactName": "xxxx",
"customerVatRegistrationCode": "AT4151511900",
"listPriceInclVat": 21.4955,
"text": " ",
"orderResponses": [
{
"orderMessageId": "7718677_1",
"orderLineMessageId": "OS0000015451",
"orderId": "OS000154",
"orderLineId": 5,
"articleId": "1911200",
"quantity": 2,
"quantityNotShipped": 0,
"reasonForNotShippedCode": null,
"reasonForNotShipped": null,
"shipmentDate": "2019-10-04T00:00:00.000Z",
"deliveryNoteId": null,
"trackingIds": [
{
"trackingId": null,
"quantityRefToTracking": "2",
"weightRefToTracking": "0.0"
}
],
"type": "orderresponse",
"filepath": "xxxORDERRESP_20191004131209.xml",
"_id": "OS005451"
},
{
"orderMessageId": "753_21",
"orderLineMessageId": "OS015451",
"orderId": "O00154",
"orderLineId": 5,
"articleId": "100200",
"quantity": 0,
"quantityNotShipped": 2,
"reasonForNotShippedCode": "01",
"reasonForNotShipped": "Out of Stock",
"shipmentDate": null,
"deliveryNoteId": null,
"trackingIds": [
{
"trackingId": null,
"quantityRefToTracking": "0",
"weightRefToTracking": "0.0"
}
],
"type": "orderresponse",
"filepath": "xxxxORDERRESP_20190618161529.xml",
"_id": "OS0000015451"
}
]
}
],
"filepath": "xxxxxORDER_7539563_20190618_071522.xml"
}
I want to match all documents, where all documents in the array LinVw, match the following condition:
{'$or': [{'LinVw.orderResponses': {'$exists': False}}, {'LinVw.orderResponses.shipmentDate': {'$type': 10}}]}
To put it in words: I want to match documents, if the array LinVw.orderResponses doesn't exist, or it contains only documents, that don't have a valid shipmentDate.
Currently I have this (using pymongo):
result = order_collection.aggregate([
{"$unwind": "$LinVw"},
{"$match": {'$or': [{'LinVw.orderResponses': {'$exists': False}}, {'LinVw.orderResponses.shipmentDate': {'$type': 10}}]}}
])
But of course this doesn't consider that all documents inside LinVw.orderResponses should match the condition.
Most examples our there don't deal with this kind of nesting and I was unable to rewrite them accordingly.
I would appreciate any help.
You can achieve this by adding a $redact stage.
Inside $redact stage, you write down the query matching documents which you want to ignore(having invalid shipMent date). That's it.
I think I did it:
result = order_collection.aggregate([
{"$unwind": "$LinVw"},
{"$match": {'$or': [{'LinVw.orderResponses': {'$exists': False}}, {'LinVw.orderResponses.shipmentDate': {'$type': 10}}]}},
{"$match": {'LinVw.orderResponses.shipmentDate': {"$not":{'$type': 9}}}},
{"$project":{"_id":0, "LinVw.orderLineMessageId":1, "LinVw.orderResponses":1}}
])

Convert .rtf file into NSDictionary or NSArray

I have a file (.rtf) that contains data that looks like:
[
{
"Symbol": "PIH",
"Name": "1347 Property Insurance Holdings, Inc.",
"LastSale": "6.8299",
"MarketCap": 41717882.9375,
"ADR TSO": "n/a",
"IPOyear": "2014",
"Sector": "Finance",
"Industry": "Property-Casualty Insurers",
"Summary Quote": "http://www.nasdaq.com/symbol/pih",
"FIELD10": ""
},
{
"Symbol": "FLWS",
"Name": "1-800 FLOWERS.COM, Inc.",
"LastSale": "9.27",
"MarketCap": 606557031.66,
"ADR TSO": "n/a",
"IPOyear": "1999",
"Sector": "Consumer Services",
"Industry": "Other Specialty Stores",
"Summary Quote": "http://www.nasdaq.com/symbol/flws",
"FIELD10": ""
},
{
"Symbol": "FCCY",
"Name": "1st Constitution Bancorp (NJ)",
"LastSale": "12.99",
"MarketCap": 103169203.98,
"ADR TSO": "n/a",
"IPOyear": "n/a",
"Sector": "Finance",
"Industry": "Savings Institutions",
"Summary Quote": "http://www.nasdaq.com/symbol/fccy",
"FIELD10": ""
}
]
Now, I would like to convert this data into something (NSDictionary, NSArray, etc.) that I can use to easily access the data. Any help would be appreciated.
guard let path = NSBundle.mainBundle().pathForResource("fileName", ofType: "txt") else {
return nil
}
do {
let content = try String(contentsOfFile:path, encoding: NSUTF8StringEncoding)
return content
} catch _ as NSError {
return nil
}
Save the .rtf file into a location (say desktop).
Read the file from that location

Swift Sorting Array<Array<AnyObject>>

I have the below response. how to sort according to the first element. i.e. [["Albania", "AE"], ["United States", "US"]]
[
[
"United States",
"US"
],
[
"Albania",
"AE"
]
]
var array = [ [ "United States", "US" ], [ "Albania", "AE" ] ]
array.sortInPlace({$0[0] < $1[0]})
One easy way to achieve this using Closures.
Here is a Shorthand way of sorting.
let array = [["United States","US"],["Albania", "AE"],["India", "IN"],["United Kingdom", "UK"]]
print(array)
let arrAscending = array.sort({($0[0]) < ($1[0])})
let arrDescending = array.sort({($0[0]) > ($1[0])})
print(arrAscending)
print(arrDescending)
Try
var a = [ [ "United States", "US" ], [ "Albania", "AE" ] ]
a.sort { (first, second) -> Bool in
return first.first < second.first
}