Show Dyanmic Multiple Lines on Chart in Swift - ios

I am working on app in which I have to show the json data on charts which is a swift framework I am using. I am able to show the data correctly on the charts but can't able to make the function dynamic. So that any n numbers of charts I can show on the LineChart graph.This is the library I am using https://github.com/danielgindi/Charts
.
This is the function I am using to show the datas on graph
func newLineChartValue(carbonLineChartValue: [LineChartValue]) {
for value in self.carbonLineChartValue {
self.zoneNames.append(value.Zone_name!)
self.allValueArray.append(value.value_array!)
self.colors.append(value.zone_color!)
}
var lineChartEntry1 = [ChartDataEntry]()
var lineChartEntry2 = [ChartDataEntry]()
var lineChartEntry3 = [ChartDataEntry]()
let firstLineArray = allValueArray[0]
print("FirstLine Data: \(firstLineArray)")
var timeLbl = [String]()
var valueOne = [Double]()
for lbl in firstLineArray {
timeLbl.append(lbl.label!)
valueOne.append(lbl.value!)
print("Time1:-> \(timeLbl)")
print("Value1:-> \(valueOne)")
}
for i in 0..<timeLbl.count {
lineChartEntry1.append(ChartDataEntry(x: Double(i), y: Double(valueOne[i])))
}
let set1 = LineChartDataSet(entries: lineChartEntry1, label: "")
set1.axisDependency = .left
set1.mode = .cubicBezier
set1.setColor(UIColor.init(hex: colors[0]))
set1.fillColor = UIColor.init(hex: colors[0])
set1.setCircleColor(UIColor.init(hex: colors[0]))
set1.lineWidth = 4
set1.circleRadius = 4
set1.fillAlpha = 65/255
set1.drawCircleHoleEnabled = false
let secondLineArray = allValueArray[1]
var valueTwo = [Double]()
for value in secondLineArray {
valueTwo.append(value.value!)
print("Value2:-> \(valueTwo)")
}
for i in 0..<valueTwo.count {
lineChartEntry2.append(ChartDataEntry(x: Double(i), y: Double(valueTwo[i])))
}
let set2 = LineChartDataSet(entries: lineChartEntry2, label: "")
set2.mode = .cubicBezier
set2.axisDependency = .left
set2.setColor(UIColor.init(hex: colors[1]))
set2.fillColor = UIColor.init(hex: colors[1])
set2.setCircleColor(UIColor.init(hex: colors[1]))
set2.lineWidth = 4
set2.circleRadius = 4
set2.fillAlpha = 65/255
set2.drawCircleHoleEnabled = false
let thirdLine = allValueArray[2]
var valueThree = [Double]()
for value in thirdLine {
valueThree.append(value.value!)
print("Value3:-> \(valueThree)")
}
for i in 0..<valueThree.count {
lineChartEntry3.append(ChartDataEntry(x: Double(i), y: Double(valueThree[i])))
}
let line3 = LineChartDataSet(entries: lineChartEntry3, label: "")
line3.axisDependency = .left
line3.mode = .cubicBezier
line3.setColor(UIColor.init(hex: colors[2]))
line3.fillColor = UIColor.init(hex: colors[2])
line3.setCircleColor(UIColor.init(hex: colors[2]))
line3.lineWidth = 4
line3.circleRadius = 4
line3.fillAlpha = 65/255
line3.drawCircleHoleEnabled = false
let lineChartData = LineChartData(dataSets: [set1,set2,line3])
lineChartData.setDrawValues(false)
lineChartView.xAxis.labelPosition = .bottom
self.lineChartView.xAxis.valueFormatter = DefaultAxisValueFormatter(block: {(index, _) in
return timeLbl[Int(index)]
})
self.lineChartView.data = lineChartData
}
This is the Struct for LineChartValue
struct LineChartValue: Codable {
var Zone_name: String?
var zone_color: String?
var value_array: [LineChartValueArray]?
}
JSON Coming from Server
{
"show_Zone": true
"Zone_name": "Zone Sales",
"zone_color": "#c69f49",
"value_array": [
{
"label": "01:00 AM",
"value": 0
},
{
"label": "02:00 AM",
"value": 0
},
{
"label": "03:00 AM",
"value": 0
},
{
"label": "04:00 AM",
"value": 0
},
{
"label": "05:00 AM",
"value": 0
},
{
"label": "06:00 AM",
"value": 0
},
{
"label": "07:00 AM",
"value": 0
},
{
"label": "08:00 AM",
"value": 0
},
{
"label": "09:00 AM",
"value": 0
},
{
"label": "10:00 AM",
"value": 0
},
{
"label": "11:00 AM",
"value": 0
},
{
"label": "12:00 AM",
"value": 0
},
{
"label": "01:00 PM",
"value": 0
},
{
"label": "02:00 PM",
"value": 0
},
{
"label": "03:00 PM",
"value": 0
},
{
"label": "04:00 PM",
"value": 0
},
{
"label": "05:00 PM",
"value": 0
},
{
"label": "06:00 PM",
"value": 0
},
{
"label": "07:00 PM",
"value": 0
},
{
"label": "08:00 PM",
"value": 0
},
{
"label": "09:00 PM",
"value": 0
},
{
"label": "10:00 PM",
"value": 0
},
{
"label": "11:00 PM",
"value": 0
},
{
"label": "12:00 PM",
"value": 0
}
]
},
I want to make this function dynamic so I can show n numbers of lines on graph.

Here is your code reduced to use a loop over carbonLineChartValue instead of hardcoding accessing the first 3 elements of the array.
The first iteration of the array populates the timeLbl array to replicate you populating that array from the first element.
I've also removed all uses of ! to avoid possible crashes from force-unwrapping nil values.
Since I do not have your charts library nor several other structures referenced in your code I am not able to verify that this code will compile as written. There may be a mistake or two here from trying to translate your original code. But the general idea should be valid.
I also don't show the initial for loop where you populate self.zoneNames, self.allValueArray, and self.colors since it's not needed for this function. Put it back in if you need those results for other uses.
func newLineChartValue(carbonLineChartValue: [LineChartValue]) {
var dataSets = [LineChartDataSet]()
var timeLbl = [String]()
for (index, chartValue) in carbonLineChartValue.enumerated() {
guard let zoneName = chartValue.Zone_name, let zoneColor = chartValue.zone_color, let lineArray = chartValue.value_array else { continue }
// Skip this one if it's not to be shown
if chartValue.show_Zone == false { continue }
let lineChartEntry = [ChartDataEntry]()
if index == 0 {
timeLbl = lineArray.compactMap { $0.label }
}
for (index, value) in lineArray.enumerated() {
if let y = value.value {
lineChartEntry.append(ChartDataEntry(x: Double(index), y: y))
}
}
let set = LineChartDataSet(entries: lineChartEntry, label: "")
set.mode = .cubicBezier
set.axisDependency = .left
set.setColor(UIColor(hex: zoneColor))
set.fillColor = UIColor(hex: zoneColor)
set.setCircleColor(UIColor(hex: zoneColor))
set.lineWidth = 4
set.circleRadius = 4
set.fillAlpha = 65/255
set.drawCircleHoleEnabled = false
dataSets.append(set)
}
let lineChartData = LineChartData(dataSets: dataSets)
lineChartData.setDrawValues(false)
lineChartView.xAxis.labelPosition = .bottom
self.lineChartView.xAxis.valueFormatter = DefaultAxisValueFormatter(block: {(index, _) in
return timeLbl[Int(index)]
})
self.lineChartView.data = lineChartData
}

Related

How to group array of objects with same key value pair

I have an array of dictionaries with same key value pairs.
[
{ "amount": "10" },
{ "amount": "20" },
{ "amount": "30" },
{ "amount": "20" },
{ "amount": "10" },
{ "amount": "10" }
]
I need to group this based on same key values.
Expected sample result:
There are 3x 10's, 2x 20's and 1x 30's
How do I achieve this?
let array = [ ["amount": "10"], ["amount": "20"], ["amount": "30"], ["amount": "20"], ["amount": "10"], ["amount": "10"] ]
var result: [String: Int] = [:]
let key = "amount"
array.forEach {
guard let value = $0[key] else { return }
result[value, default: 0] += 1
}
print("\(result["10"])") // 3

Group a dictionary with same type into an array with full key and value using Swift

it's been a long time I don't use Swift.
I have a response data like this and I saved it into an array named responseData:
[
{
"type": "Switch",
"name": "Switch1",
"search_key": "is_promotion",
"key": "is_promotion=1",
"icon": ""
},
{
"type": "Switch",
"name": "Switch2",
"search_key": "shop_type",
"key": "shop_type=2",
"icon": ""
},
{
"type": "Switch",
"name": "Switch3",
"search_key": "is_certified",
"key": "is_certified=1",
"icon": ""
},
{
"type": "Switch",
"name": "Switch4",
"search_key": "shop_free_shipping",
"key": "shop_free_shipping=1",
"icon": ""
},
{
"type": "Switch",
"name": "Switch5",
"search_key": "is_loyalty",
"key": "is_loyalty=1",
"icon": ""
},
{
"type": "Switch",
"name": "Switch6",
"search_key": "is_using_instant",
"key": "is_using_instant=1",
"icon": ""
},
{
"type": "Switch",
"name": "Switch7",
"search_key": "is_installment",
"key": "is_installment=1",
"icon": ""
},
{
"type": "Range",
"name": "Price Range",
"search_key": "level_Price_Max_Min",
"value": [
{
"option_id": 0,
"option_name": 0
},
{
"option_id": 0,
"option_name": 10000000
}
]
},
{
"type": "ColorTerm",
"name": "Color",
"search_key": "color_key",
"value": [
{
"option_id": 605,
"value": "Black",
"color_id": 13,
"image": "",
"option_name": "Black",
"background": "#000000",
"option_active": "",
"facet_count": 52655
},
Now I wanna group all dictionary with type Switch into one array and I can access to the keys inside it, then present data of both Switch type array and the others type on a UITableView which have 2 section (Switch type in section 0). How can I do it? I have to search some other solution but I don't understand how to apply them to my code for work.
Here is my FilterModel class:
class FilterModel: NSObject, NSCoding, NSCopying {
override func copy(with zone: NSZone? = nil) -> Any {
// This is the reason why `init(_ model: GameModel)`
// must be required, because `GameModel` is not `final`.
let copy = FilterModel(dict: self.dictionary)
if let arrAttribute = NSArray(array: self.value , copyItems: true) as? [AttributeValueModel] {
copy.value = arrAttribute
}
return copy
}
override init(dict: Dictionary<String, Any>) {
super.init(dict: dict);
value = self.valueParse()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
var name: String? {
return self.dictionary.getString(forKey: "name")
}
var icon: String? {
return self.dictionary.getString(forKey: "icon")
}
var search_key: String? {
return self.dictionary.getString(forKey: "search_key")
}
var key: String? {
return self.dictionary.getString(forKey: "key")
}
var type: FilterDisplayType {
let type = self.dictionary.getString(forKey: "type")
return self.getType(string: type)
}
var value: [AttributeValueModel] = [];
func valueParse()-> [AttributeValueModel] {
// with switch type, Just set true or false
// Change ParentValue to Child
if type == .Switch {
let dict:Dictionary<String, AnyObject> = [
"option_id": "false" as AnyObject,
"option_name": self.name! as AnyObject,
"name": self.name! as AnyObject,
"icon": self.icon! as AnyObject
]
let item = AttributeValueModel(dict:dict);
return [item]
}
guard let childs = (self.dictionary["value"]) as? [Dictionary<String, AnyObject>]
else { return [] }
var output: [AttributeValueModel] = [];
for aDict in childs {
let item = AttributeValueModel(dict:aDict);
if type == .Range && item.option_id == "0" {
item.setRangeOptionID(aValue: item.option_name!)
}
output.append(item);
}
return output;
}
///get list AttributeValueModel Selected
func selectedValues() -> [AttributeValueModel] {
var output: [AttributeValueModel] = [];
for itemTemp in self.value {
if(itemTemp.selected){
if type == .Switch {
itemTemp.setSelectedOptionID()
}
output.append(itemTemp);
}
}
return output;
}
/// make a Filter Item from attributeValues Seleted
func getFilterItem() -> FilterItem? {
var itemFilter: FilterItem = FilterItem(key: self.search_key!, value: "")
itemFilter.key = self.search_key!
let output: NSMutableArray = [];
for attItem in self.selectedValues() {
if attItem.option_id != "" {
output.add(attItem.option_id!);
}
}
if(output.count == 0) {
return nil;
}
let valueString = output.componentsJoined(by: ",");
itemFilter.value = valueString;
return itemFilter
}
///get list AttributeValueModel Selected
func resetToDefault() -> [AttributeValueModel] {
var output: [AttributeValueModel] = [];
for itemTemp in self.value {
if(itemTemp.selected){
itemTemp.selected = false
if type == .Switch {
itemTemp.setSelectedOptionID()
}
if type == .Range {
itemTemp.setRangeOptionID(aValue: itemTemp.option_name!)
}
output.append(itemTemp);
}
}
return output;
}
//for UI
var wasExpanding = false
var numberOfRow:Int = 0
/************/
var attributeNameLength: Int {
var string = ""
for item in valueParse() {
string += item.option_name!
}
return string.count
}
var lenghtSizeName:Int {
var row:Int = 1
var width:CGFloat = 0
let padding:CGFloat = 8
let screen = screenWidth - 50 - 16
for item in valueParse() {
let size = ((item.option_name ?? "") as NSString).size(withAttributes: [
NSAttributedStringKey.font : UIFont.fontRegular_Big()
])
let totalWidth = size.width + padding + 16
if totalWidth <= CGFloat(32) {
width += 32
if width >= screen {
row += 1
width = 32
}
} else {
width += totalWidth
if width >= screen {
row += 1
width = totalWidth
}
}
}
return row
}
}
You can filter your response data to get only switches in an array.
responseData.filter {($0.type ?? "") == "Switch"}
And of course != would give you non switches.

Filter Array of Any Object Based on Object Property

I am getting a response in this format
[
"1234": {
"startDate": "1536278400",
"endDate": "1536796800",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 1
},
"adityaKumar": {
"startDate": "1536364800",
"endDate": "1540080000",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 6
},
"madhu60": {
"startDate": "1539388800",
"endDate": "1539475200",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 2
}
]
This response is of type [string:Any]. I have to sort the data using the playerRank property. I want the output to be of type [string:Any].
How is this possible in Swift?
A dictionary is unsorted
From swift documentation
A dictionary stores associations between keys of the same type and values of the same type in an collection with no defined ordering.
However, you can use the .sorted method on your dictionary which will give you an array of dictionaries (arrays are sortable).
let sorted = dictionary.sorted(by: { ($0.value["playerRank"]) < ($1.value["playerRank"]) })
From your original example sorted might look like this
[
(
"key": "madhu60",
"value": {
"startDate": "1539388800",
"endDate": "1539475200",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 2
}
)
....
]
Once you parse that JSON, you'll get a dictionary of type [String: [String:Any]], i.e.
let data = [
"1234": [
"startDate": "1536278400",
"endDate": "1536796800",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 1
],
"adityaKumar": [
"startDate": "1536364800",
"endDate": "1540080000",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 6
],
"madhu60": [
"startDate": "1539388800",
"endDate": "1539475200",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 2
]
]
You can sort this dictionary simply using sorted method, i.e.
let sortedData = data.sorted {
if let rank1 = $0.value["playerRank"] as? Int, let rank2 = $1.value["playerRank"] as? Int
{
return rank1 < rank2
}
return false
}
And you are good to go.
I show you a complete answer here.
First convert the response text to a qualified JSON Data.
Second build a customized structure to decode JSON Data to.
Third sort the structure
Last to print back to original response text (You may use a string to have the print result.)
First:
let json = """
[
"1234": {
"startDate": "1536278400",
"endDate": "1536796800",
"playerRank": 4,
"performance": 100,
"isProfit": false,
"members": 1
},
"adityaKumar": {
"startDate": "1536364800",
"endDate": "1540080000",
"playerRank": 2,
"performance": 100,
"isProfit": false,
"members": 6
},
"madhu60": {
"startDate": "1539388800",
"endDate": "1539475200",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 2
}
]
""".replacingOccurrences(of: "[", with: "{").replacingOccurrences(of: "]", with: "}").data(using: .utf8)
second:
struct Response {
struct ResponseData {
let name : String
let startDate : String
let endDate : String
let performance : Int
let playerRank : Int
let isProfit : Bool
let members : Int
}
var responseData: [ResponseData]
init (responseData: [ResponseData] = []){
self.responseData = responseData
}
}
extension Response: Decodable {
struct DataKey: CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int? { return nil }
init?(intValue: Int) { return nil}
static let startDate = DataKey(stringValue: "startDate")!
static let endDate = DataKey(stringValue: "endDate")!
static let performance = DataKey(stringValue: "performance")!
static let playerRank = DataKey(stringValue: "playerRank")!
static let isProfit = DataKey(stringValue: "isProfit")!
static let members = DataKey(stringValue: "members")!
}
public init(from decoder: Decoder) throws {
var responseData = [ResponseData]()
let container = try decoder.container(keyedBy: DataKey.self)
for key in container.allKeys {
let dataContainer = try container.nestedContainer(keyedBy: DataKey.self, forKey: key)
let startDate = try dataContainer.decode(String.self, forKey: .startDate)
let endDate = try dataContainer.decode(String.self, forKey: .endDate)
let performance = try dataContainer.decode(Int.self, forKey: .performance)
let playerRank = try dataContainer.decode(Int.self, forKey: .playerRank)
let isProfit = try dataContainer.decode(Bool.self, forKey: .isProfit)
let members = try dataContainer.decode(Int.self, forKey: .members)
let each = ResponseData.init(name: key.stringValue, startDate: startDate, endDate: endDate, performance: performance, playerRank: playerRank, isProfit: isProfit, members: members)
responseData.append(each)
}
self.init(responseData: responseData)
}
}
let decoder = JSONDecoder()
var decodedStore = try decoder.decode(Response.self, from: json!)
third:
decodedStore.responseData.sort{$0.playerRank < $1.playerRank }
last:
print ("[")
for each in decodedStore.responseData {
print ("""
\(each.name): {
"startDate": \(each.startDate),
"endDate": \(each.endDate),
"playerRank": \(each.playerRank),
"performance": \(each.performance),
"isProfit": \(each.isProfit),
"members": \(each.members)
}
""")
}
print ("]\n")

swift 4 split array and filter

i will build a UICollectionView with sections.
The sections are based on the return value from json.category.
the json format is like:
[{"id":"1",
"name":"Apple",
"category":"Fruits"},
{"id":"2",
"name":"Pie",
"category":"Fruits"},
{"id":"3",
"name":"Tomato",
"category":"Vegetable"}]
I need a array filter hat the array is something like: (for sectionsItems and sectionNames)
CategorieNames[STRING] = ["Fruits","Vegetable"] // the section names from json.category
Fruits = [STRING] = ["Apple","Pie"]
Vegetables = [STRING] = ["Tomato"]
Categories.append[Fruits]
Categories.append[Vegetables]
Categories[[STRING]] = [[Fruits],[Vegetable]]
Try bellow code.
let arrData = [["id": "1",
"name": "Apple",
"category": "Fruit"],
["id": "2",
"name": "Pie",
"category": "Fruit"],
["id": "3",
"name": "Tomato",
"category": "Vegetable"]]
let categorieNames = Array(Set(arrData.map({$0["category"]!})))
var arrResult:[[String]] = []
for i in 0..<categorieNames.count {
let categories = arrData.filter({$0["category"] == categorieNames[i]}).map({$0["name"]!})
arrResult.append(categories)
}
print("result : \(arrResult)")
result : [["Apple", "Pie"], ["Tomato"]]
you can do it as follows:
let arrData = [["id": "1",
"name": "Apple",
"category": "Fruit"],
["id": "2",
"name": "Pie",
"category": "Fruit"],
["id": "3",
"name": "Tomato",
"category": "Vegetable"]]
var categorys = [[String]]()
var fruits = [String]()
var vegetable = [String]()
for data in arrData {
if let category = data["category"] {
if category == "Fruit"{
if let aFruit = data["name"] {
fruits.append(aFruit)
}
}
else if category == "Vegetable" {
if let aVeggeie = data["name"] {
vegetable.append(aVeggeie)
}
}
}
}
categorys.append(fruits)
categorys.append(vegetable)

Swift2 - Using Alamofire 3 in nested JSON

I have the following JSON:
{
"_embedded": {
"modifier_groups": [
{
"_embedded": {
"options": [
{
"id": "8kT9KTX7",
"name": "Perfect",
"open": false,
"pos_id": "8kT9KTX7",
"price_per_unit": 0
},
{
"id": "zRcEkcj8",
"name": "Overcooked",
"open": false,
"pos_id": "zRcEkcj8",
"price_per_unit": 0
}
]
},
"id": "eMiy4iR4",
"maximum": 1,
"minimum": 1,
"name": "Temperature",
"required": false
},
{
"_embedded": {
"options": [
{
"id": "E5cpac84",
"name": "Tomato",
"open": false,
"pos_id": "E5cpac84",
"price_per_unit": 0
},
{
"id": "GkiREiyL",
"name": "Cheese",
"open": false,
"pos_id": "GkiREiyL",
"price_per_unit": 100
}
]
},
"id": "kMT85Tay",
"maximum": null,
"minimum": 1,
"name": "Toppings",
"required": false
}
]
},
"count": 2,
"limit": 20
}
So there are modifier group names (e.g. "Temperature" and "Toppings"), and group options (e.g. "Perfect" and "Overcooked" for "Temperature" group).
What I am trying to do is build a [String] such as:
["Temperature - Perfect", "Temperature - Overcooked", "Toppings - Tomato", "Toppings - Cheese"]
What would be the quickest way to go about that?
Currently, I first extract the groups into a [String] using valueForKeyPath:
Alamofire.request(.GET, url, headers: headers, encoding: .JSON)
.responseJSON { response in
switch response.result {
case .Success(let JSON):
let jsonData = JSON as? NSDictionary
let groupNames = jsonData?.valueForKeyPath("_embedded.modifier_groups.name")
But how would I get from there to drilling deeper into the group options so that I append them into the [String]?
UPDATE
I tried this but it's not returning anything:
var mods = [String]()
let modGroups = jsonData?.valueForKeyPath("_embedded.modifier_groups")
if let modGroups = modGroups {
for modGroup in modGroups as! [AnyObject] {
let groupOptions = modGroups.valueForKeyPath("_embedded.options")
if let groupOptions = groupOptions {
for groupOption in groupOptions as! [AnyObject] {
mods.append("\(modGroup) - \(groupOption)")
}
}
}
}
Got it:
var mods = [String]()
let modGroups = jsonData?.valueForKeyPath("_embedded.modifier_groups") as? [NSDictionary]
if let modGroups = modGroups {
for modGroup in modGroups {
let groupOptions = modGroup.valueForKeyPath("_embedded.options") as? [NSDictionary]
if let groupOptions = groupOptions {
for groupOption in groupOptions {
mods.append("\(modGroup.valueForKey("name")!) - \(groupOption.valueForKey("name")!)")
}
}
}
}

Resources