Sort dictionary using ordered array - sorting

I have a dictionary which has a key I'd like to do multiple sort on.
One key, however, can't be sorted alphabetically or it would not make sense.
Those keys can have only a defined set of values, and I'm trying to sort using a specified order.
The second sort is easy, so I can use the sort descriptor way, which works fine:
let costSort = NSSortDescriptor( key: "cmc", ascending: false )
let sortDescriptors = [costSort]
let sortedCards = cards.sortedArrayUsingDescriptors( sortDescriptors )
The other key must be ordered in particular order specified by an array:
let rarity = ["Special", "Mythic rare", "Rare", "Uncommon", "Common", "Basic Land"]
So, not in alphabetical order at all, yet I need to make sure that the sort follows this ordering.
I'm unsure how to code it.
Example records:
let cards = [
["name": "card1", "rarity": "Common", "cmc": 2],
["name": "card2", "rarity": "Rare", "cmc": 4],
["name": "card3", "rarity": "Common", "cmc": 1],
["name": "card4", "rarity": "Mythic rare", "cmc": 8]
]
The goal is that the sorted result, using [raritySort,costSort] would be:
card4
card2
card1
card3

If you want to use the NSArray sort methods, you can do something like this using an NSSortDescriptor based on a custom NSComparator that looks up the indexes of the rarities in your array, and uses them to determine relative rank/ordering:
let rarities = ["Special", "Mythic rare", "Rare", "Uncommon", "Common", "Basic Land"]
let cards = [
["name": "card1", "rarity": "Common", "cmc": 2],
["name": "card2", "rarity": "Rare", "cmc": 4],
["name": "card3", "rarity": "Common", "cmc": 1],
["name": "card5", "rarity": "Common", "cmc": 7],
["name": "card6", "rarity": "Common", "cmc": 0],
["name": "card4", "rarity": "Mythic rare", "cmc": 8]
]
let nsarrayCards = cards as NSArray
let costSort = NSSortDescriptor( key: "cmc", ascending: false )
let raritySort = NSSortDescriptor(key: "rarity", ascending: false, comparator: { (leftRarity, rightRarity) -> NSComparisonResult in
if let leftRarityIdx = find(rarities, leftRarity as String) {
if let rightRarityIdx = find(rarities, rightRarity as String) {
if rightRarityIdx == leftRarityIdx {
return .OrderedSame
}
return leftRarityIdx > rightRarityIdx ? .OrderedAscending : .OrderedDescending
}
}
return .OrderedSame
})
let sortDescriptors = [raritySort, costSort]
let sortedCards = nsarrayCards.sortedArrayUsingDescriptors( sortDescriptors )
At the end here, sortedCards contains:
[{
cmc = 8;
name = card4;
rarity = "Mythic rare";
}, {
cmc = 4;
name = card2;
rarity = Rare;
}, {
cmc = 7;
name = card5;
rarity = Common;
}, {
cmc = 2;
name = card1;
rarity = Common;
}, {
cmc = 1;
name = card3;
rarity = Common;
}, {
cmc = 0;
name = card6;
rarity = Common;
}]

Here's a possible solution. Is uses Swift's sorted method and a closure instead of an NSSortDescriptor.
var cards = [
["name": "card1", "rarity": "Common", "cmc": 2],
["name": "card2", "rarity": "Rare", "cmc": 4],
["name": "card3", "rarity": "Common", "cmc": 1],
["name": "card4", "rarity": "Mythic rare", "cmc": 8]
]
let rarity = ["Special", "Mythic rare", "Rare", "Uncommon", "Common", "Basic Land"]
var sortedCards = sorted(cards) {
find(rarity, $0["rarity"] as String) > find(rarity, $1["rarity"] as String) || $0["cmc"] as Int >= $1["cmc"] as Int
}
println(sortedCards)
I used the more concise closure syntax here. You could also expand it like this:
var sortedCards = sorted(cards, { (d1, d2) -> Bool in
if find(rarity, d1["rarity"] as String) > find(rarity, d2["rarity"] as String){
return true
}
return d1["cmc"] as Int >= d2["cmc"] as Int
})

What about this?
let sortedCards = cards.sorted { (d1, d2) -> Bool in
d1.valueForKey("cmc") as Int > d2.valueForKey("cmc") as Int
}
Ok, what about this?
var cards = [
["name": "card1", "rarity": "Common", "cmc": 2],
["name": "card2", "rarity": "Rare", "cmc": 4],
["name": "card3", "rarity": "Common", "cmc": 1],
["name": "card4", "rarity": "Mythic rare", "cmc": 8]
]
let rarity = ["Special": 1, "Mythic rare": 2, "Rare": 3, "Uncommon": 4, "Common": 5, "Basic Land": 6]
let sortedCards = cards.sorted { (d1, d2) -> Bool in
let d1R: Int = rarity[d1.valueForKey("rarity") as String]!
let d2R: Int = rarity[d2.valueForKey("rarity") as String]!
if d1R == d2R {
return d1.valueForKey("cmc") as Int > d2.valueForKey("cmc") as Int
} else {
return d1R < d2R
}
}

Related

Refresh Text within Swift UI after 1st run

I am racking my brain trying to get this code working, I am just starting out on SwiftUI and haven't found a solution. I have a math equation that adds the variables and accepts the answer input from the user. My issue is once the answer is correct, how can i get the variables numbers1 & numbers2 to refresh and grab new numbers and give another math problem?
import SwiftUI
import Foundation
struct ContentView: View {
#State private var answer = ""
#State private var number1 = ""
#State private var number2 = ""
#State private var messageText = ""
#State private var keyboardHeight: CGFloat = 0.0
var numbers1 : Int = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].randomElement()!
var numbers2 : Int = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].randomElement()!
var questionNumber = 0
var value = 0.00
var body: some View {
VStack {
ForEach((1...10).reversed(), id: \.self) {_ in
Text(self.number2)
}
Text("\(numbers1)")
.font(.largeTitle)
.padding()
Text("\(numbers2)")
.font(.largeTitle)
.padding()
TextField("answer", text: self.$answer)
.padding(/*#START_MENU_TOKEN#*/.all/*#END_MENU_TOKEN#*/)
.font(/*#START_MENU_TOKEN#*/.largeTitle/*#END_MENU_TOKEN#*/)
//.keyboardType(.numberPad)
Button(action: {
var addition: Int {
let addition = self.numbers1 + self.numbers2
return addition
}
print("submit button pressed")
print(addition)
if (self.answer == String(addition)) {
print("Answer is Correct!")
}
else {
print("Answer is Incorrect")
}
self.answer = ""
}) {
Text("Submit")
.font(.largeTitle)
.padding()
.background(Color.red)
.foregroundColor(.white)
.cornerRadius(40)
}
// .padding()
.actionSheet(isPresented: /*#START_MENU_TOKEN#*/ /*#PLACEHOLDER=Is Presented#*/.constant(false)/*#END_MENU_TOKEN#*/) {
ActionSheet(title: Text(/*#START_MENU_TOKEN#*/ /*#PLACEHOLDER=Title#*/"Action Sheet"/*#END_MENU_TOKEN#*/))
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
```
enter code here
what you can do is this:
declare
#State var numbers1 : Int = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].randomElement()!
#State var numbers2 : Int = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].randomElement()!
and
if (self.answer == String(addition)) {
print("Answer is Correct!")
self.numbers1 = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].randomElement()!
self.numbers2 = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].randomElement()!
}
or some variation of this, like storing the array in a var.

adding function to loops through

I need to search in a big json nested collection which have unique IDs recursively. The collection contains key values or nested arrays which contains keys. Keys can be anywhere in the object. Keys can be number or string.
Please note: Key values are unique if they are not in array. If they are in array, the key duplicates per items in array. For example,
"WebData": {
WA1: 3, //not in array so unique
WA3: 2, so unique
WA3: "NEO",
WebGroup : [
{ Web1: 1, //duplicate Web1
Web2: 2
},
{ Web1: 2, //duplicate Web2
Web2: 2
}]
}
What I want:
I will pass an array of keys in different variations for example
Not in Arrays: I will pass key return either their values or sum for example:
function(["WA1",""WA3", "RAE1"],"notsum")
If I pass (not sum)
["WA1",""WA3", "RAE1"]
and the operation is not "sum", it should return an array of their values from the collection
[3,2,1]
If I pass the same but operation is sum)
function(["WA1",""WA3", "RAE1"],"sum")
["WA1",""WA3", "RAE1"]
it should return sum from the collection
return 6
If in Array: If the value to search are in the array means they duplicate, then it should return me sum or their individual values again For example
["WEB1","Web2"]
. It could either return me,
[7,1] //Again total of 3+4, 0+1 //see in example
or
[[3,4],[0,1]] //Because values are duplicate and in array, just collect them
I need to do in an elegant way:
Full example of JSON:
{
version: "1.0"
submission : "editing"
"WebData": {
WA1: 3,
WA3: 2,
WA3: "NEO",
WebGroup : [
{ Web1: 3,
Web2: 0
},
{ Web1: 4,
Web2: 1
}]
},
"NonWebData": {
NWA1: 3,
NWA2: "INP",
NWA3: 2,
},
"FormInputs": {
FM11: 3,
FM12: 1,
FM13: 2,
"RawData" : {
"RawOverview": {
"RAE1" : 1,
"RAE2" : 1,
},
"RawGroups":[
{
"name": "A1",
"id": "1",
"data":{
"AD1": 'period',
"AD2": 2,
"AD3": 2,
"transfers": [
{
"type": "in",
"TT1": 1,
"TT2": 2,
},
{
"type": "out",
"TT1": 1,
"TT2": 2,
}
]
}
},
{
"name": "A2",
"id": "2",
"data":{
"AD1": 'period',
"AD2": 2,
"AD3": 2,
"transfers": [
{
"type": "in",
"TT1": 1,
"TT2": 2,
},
{
"type": "out",
"TT1": 1,
"TT2": 2,
}
]
}
}
]
},
"Other":
{ O1: 1,
O2: 2,
O3: "hello"
},
"AddedBy": "name"
"AddedDate": "11/02/2019"
}
I am not able to write a function here, which can do this for me, my code is simply searching in this array, and I loop through to find it, which is I am sure not the correct way.
My code is not elegant, and I am using somehow repetitive functions. This is just one snippet, to find out the keys in one level. I want only 1 or 2 functions to do all this
function Search(paramKey, formDataArray) {
var varParams = [];
for (var key in formDataArray) {
if (formDataArray.hasOwnProperty(key)) {
var val = formDataArray[key];
for (var ikey in val) {
if (val.hasOwnProperty(ikey)) {
if (ikey == paramKey)
varParams.push(val[ikey]);
}
}
}
}
return varParams;
}
One more test case if in Array: to Return only single array of values, without adding. (Update - I achieved this through editing the code following part)
notsumsingle: function (target, key, value) {
if (target[key] === undefined) {
target[key] = value;
return;
}
target.push(value);
},
"groupData": [
{
"A1G1": 1,
"A1G2": 22,
"AIG3": 4,
"AIG4": "Rob"
},
{
"A1G1": 1,
"A1G2": 41,
"AIG3": 3,
"AIG4": "John"
},
{
"A1G1": 1,
"A1G2": 3,
"AIG3": 1,
"AIG4": "Andy"
}
],
perform(["AIG2",""AIG4"], "notsum")
It is returning me
[
[
22,
41,
3
]
],
[
[
"",
"Ron",
"Andy"
]
]
Instead, can I add one more variation "SingleArray" like "sum" and "notsum" and get the result as single Array.
[
22,
41,
3
]
[
"",
"Ron",
"Andy"
]
4th one, I asked, is it possible the function intelligent enough to pick up the sum of arrays or sum of individual fields automatically. for example, in your example, you have used "sum" and "total" to identify that.
console.log(perform(["WA1", "WA3", "RAE1"], "total")); // 6
console.log(perform(["Web1", "Web2"], "sum")); // [7, 1]
Can the function, just use "sum" and returns single or array based on if it finds array, return [7,1] if not return 6
5th : I found an issue in the code, if the json collection is added this way
perform(["RAE1"], "notsum") //[[1,1]]
perform(["RAE1"], "sum") //2
It returns [1, 1], or 2 although there is only one RAE1 defined and please note it is not an array [] so it should not be encoded into [[]] array, just the object key
"RawData" : {
"RawOverview": {
"RAE1" : 1,
"RAE2" : 1,
}
For making it easier, and to take the same interface for getting sums or not sums and a total, without any array, you could introduce another operation string total for getting the sum of all keys.
This approach takes an object for getting a function which either add an value to an array at the same index or stores the value at an specified index, which match the given keys array of the function.
For iterating the object, you could take the key/value pairs and iterate until no more object is found.
As result, you get an array, or the total sum of all items.
BTW, the keys of an object are case sensitive, for example 'WEB1' does not match 'Web1'.
function perform(keys, operation) {
function visit(object) {
Object
.entries(object)
.forEach(([k, v]) => {
if (k in indices) return fn(result, indices[k], v);
if (v && typeof v === 'object') visit(v);
});
}
var result = [],
indices = Object.assign({}, ...keys.map((k, i) => ({ [k]: i }))),
fn = {
notsum: function (target, key, value) {
if (target[key] === undefined) {
target[key] = value;
return;
}
if (!Array.isArray(target[key])) {
target[key] = [target[key]];
}
target[key].push(value);
},
sum: function (target, key, value) {
target[key] = (target[key] || 0) + value;
}
}[operation === 'total' ? 'sum' : operation];
visit(data);
return operation === 'total'
? result.reduce((a, b) => a + b)
: result;
}
var data = { version: "1.0", submission: "editing", WebData: { WA1: 3, WA3: 2, WAX: "NEO", WebGroup: [{ Web1: 3, Web2: 0 }, { Web1: 4, Web2: 1 }] }, NonWebData: { NWA1: 3, NWA2: "INP", NWA3: 2 }, FormInputs: { FM11: 3, FM12: 1, FM13: 2 }, RawData: { RawOverview: { RAE1: 1, RAE2: 1 }, RawGroups: [{ name: "A1", id: "1", data: { AD1: 'period', AD2: 2, AD3: 2, transfers: [{ type: "in", TT1: 1, TT2: 2 }, { type: "out", TT1: 1, TT2: 2 }] } }, { name: "A2", id: "2", data: { AD1: 'period', AD2: 2, AD3: 2, transfers: [{ type: "in", TT1: 1, TT2: 2 }, { type: "out", TT1: 1, TT2: 2 }] } }] }, Other: { O1: 1, O2: 2, O3: "hello" }, AddedBy: "name", AddedDate: "11/02/2019" };
console.log(perform(["WA1", "WA3", "RAE1"], "notsum")); // [3, 2, 1]
console.log(perform(["WA1", "WA3", "RAE1"], "total")); // 6
console.log(perform(["Web1", "Web2"], "sum")); // [7, 1]
console.log(perform(["Web1", "Web2"], "notsum")); // [[3, 4], [0, 1]]
.as-console-wrapper { max-height: 100% !important; top: 0; }

multi-line chart animation code modify google charts

I found the code from here:
On load Google LineChart animation
google.load('visualization', '1', { packages: ['corechart'], callback: function() {
var data = new google.visualization.DataTable();
data.addRows(5);
data.addColumn('string', '');
data.addColumn('number', 'Sales');
data.addRows(5);
data.setValue(0, 0, 'Jan');
data.setValue(1, 0, 'Feb');
data.setValue(2, 0, 'Mar');
data.setValue(3, 0, 'Apr');
data.setValue(4, 0, 'May');
var options = {
title: 'Sales by months for 2013 year', curveType: 'function',
"vAxis": { "minValue": "0", "maxValue": 6 }, "hAxis": { "slantedTextAngle": "45", "slantedText": "true" }, "legend": { "position": "top" }, "pointSize": "5",
animation: { duration: 250 }
};
var chart = new google.visualization.LineChart(document.getElementById('test'));
var index = 0;
var chartData = [ 5, 1, 4, 2, 3 ]
var drawChart = function() {
console.log('drawChart index ' + index);
if (index < chartData.length) {
data.setValue(index, 1, chartData[index++]);
chart.draw(data, options);
}
}
google.visualization.events.addListener(chart, 'animationfinish', drawChart);
chart.draw(data, options);
drawChart();
}});
If I want to create multi-line, how to modify this code? Thanks!
I'm not a javascript programmer and not familiar with OOP
The author is OneMoreVladimir, but I don't have the access to comment under his post.
google.load('visualization', '1', {
packages: ['corechart'],
callback: function() {
var data = new google.visualization.DataTable();
data.addRows(5);
data.addColumn('string', '');
data.addColumn('number', 'Sales');
data.addColumn('number', 'Sales2');
data.addRows(5);
data.setValue(0, 0, 'Jan');
data.setValue(1, 0, 'Feb');
data.setValue(2, 0, 'Mar');
data.setValue(3, 0, 'Apr');
data.setValue(4, 0, 'May');
var options = {
title: 'Sales by months for 2013 year',
curveType: 'function',
"vAxis": {
"minValue": "0",
"maxValue": 8
},
"hAxis": {
"slantedTextAngle": "45",
"slantedText": "true"
},
"legend": {
"position": "top"
},
"pointSize": "5",
animation: {
duration: 600
}
};
var chart = new google.visualization.LineChart(document.getElementById('test'));
var index = 0;
var chartData = [5, 1, 4, 2, 3]
var drawChart = function() {
console.log('drawChart index ' + index);
if (index < chartData.length) {
data.setValue(index, 1, chartData[index++]);
chart.draw(data, options);
}
}
var index1 = 0;
var chartData1 = [1, 3, 4, 6, 5]
var drawChart1 = function() {
console.log('drawChart1 index1 ' + index1);
if (index1 < chartData1.length) {
data.setValue(index1, 2, chartData1[index1++]);
chart.draw(data, options);
}
}
google.visualization.events.addListener(chart, 'animationfinish', drawChart);
google.visualization.events.addListener(chart, 'animationfinish', drawChart1);
chart.draw(data, options);
drawChart();
drawChart1();
}
});
I figure out! Here is the code.
But another question, the code pass the http://jsfiddle.net and run very well, but when I copy paste it into the HTML why it doesn't work? Did I miss something?

Handsontable cross calculations

I have a handontable demo.
document.addEventListener("DOMContentLoaded", function() {
var
example = document.getElementById('example1'),
hot1;
hot1 = new Handsontable(example, {
data: [
['', '', '', ''],
[1, 2, 3, '=SUM(A2:C2)'],
[1, 2, 3],
],
width: 584,
height: 320,
rowHeaders: true,
formulas: true,
colHeaders: true,
columns: [1, 2, 3, 4],
columnSummary: function () {
var summary = [];
for (var i = 0; i < 4; i++) {
summary.push({
ranges: [[1, 2]],
destinationRow: 0,
destinationColumn: i,
type: 'sum',
forceNumeric: true,
sourceColumn: i
});
}
return summary;
}
});
});
It caclulates:
Sum of column and puts a result in the first raw.
Sum of rows (except first one) and puts it in the column "D"
I need to calculate correct total of the totals, which is the cell D1.
After loading and changing any cell calculation of D1 has to work properly.
Thank you for any ideas.
The option columnSummary should not be applied on the 4th column (the column of SUM results). Try to apply you code of columnSummary option only for the first three columns :
for (var i = 0; i < 3; i++) //Instead of i < 4
And use in the row one what you use to calculate the sum on your other rows :
data: [
['', '', '', '=SUM(A1:C1)'],
[1, 2, 3, '=SUM(A2:C2)'],
[1, 2, 3, '=SUM(A3:C3)'],
],
You will see that it works like a charm : JSFiddle.

Conditional visibility of Yahoo widgets inside a datatable

I have the following code (see below). The question is how to hide the formatter textbox for column quantity_value1 whenever the value of the column condition1 is 0.
var response = {
"id": 1,
"items": [
{"condition1": 1, "quantity_value1":"value1"},
{"condition1": 1, "quantity_value1":"value2"},
{"condition1": 0, "quantity_value1":"value3"},
{"condition1": 1, "quantity_value1":"value4"},
{"condition1": 0, "quantity_value1":"value5"}
]
};
var bpColumns = [
{label:'header1', children:[
{
key:"condition1"
},
{
key:"quantity_value1",
formatter:YAHOO.widget.DataTable.formatTextbox
}]}
];
YAHOO.example.Data = response;
this.bpDataSource = new YAHOO.util.DataSource(YAHOO.example.Data);
this.bpDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
this.bpDataSource.responseSchema = {
resultsList: "items",
fields: ["condition1","quantity_value1"]
};
this.standardSelectDataTable = new YAHOO.widget.ScrollingDataTable("mydiv",
bpColumns, this.bpDataSource, {height:"15em"});
You will have to write your own formatter as shown here in the user guide. There is an example here. There is no way to configure the built-in one, but you might use it as a the basis for your version, search for formatTextbox in the source.
This works:
var response = {
"id": 1,
"items": [
{"condition1": 1, "quantity_value1":"value1"},
{"condition1": 1, "quantity_value1":"value2"},
{"condition1": 0, "quantity_value1":"value3"},
{"condition1": 1, "quantity_value1":"value4"},
{"condition1": 0, "quantity_value1":"value5"}
]
};
var bpColumns = [
{label:'header1', children:[
{
key:"condition1"
},
{
key:"quantity_value1",
formatter:"formatTextbox"
}]}
];
var vrecord = null;
YAHOO.example.Data = response;
this.formatTextbox = function(el, oRecord, oColumn, oData, oDataTable) {
if (oRecord.getData("condition1") === 1){
var value = (YAHOO.lang.isValue(oData)) ? YAHOO.lang.escapeHTML(oData.toString()) : "";
var input = document.createElement("input");
input.type = "text";
input.value = value;
input.onchange = function()
{
vrecord.setData("quantity_value1",this.value);
console.log(vrecord);
};
el.appendChild(input);
}
};
// Add the custom formatter to the shortcuts
YAHOO.widget.DataTable.Formatter.formatTextbox = this.formatTextbox;
this.bpDataSource = new YAHOO.util.DataSource(YAHOO.example.Data);
this.bpDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
this.bpDataSource.responseSchema = {
resultsList: "items",
fields: ["condition1","quantity_value1"]
};
this.standardSelectDataTable = new YAHOO.widget.ScrollingDataTable("mydiv",
bpColumns, this.bpDataSource, {height:"15em"});
this.standardSelectDataTable.subscribe("rowClickEvent", this.standardSelectDataTable.onEventSelectRow);
this.standardSelectDataTable.subscribe("rowClickEvent", function(event, target) {
var selectedRows = this.getSelectedRows();
vrecord = this.getRecord(selectedRows[0]);
});

Resources