Sort Array of Dictionaries by NSDates within the Dictionaries - xcode

I am creating an application with separate posts in it (similar to Instagram). Each post is a dictionary located in one big array. Within each dictionary their is an NSDate which I would like to sort the dictionaries by in order from newest to oldest.
I have read similar questions but they are in objective C. I have a feeling NSSortDescriptor be a helpful method but am unsure on how to use it in this situation.
Any help would be great,
Thanks

If you’re writing in Swift, you’ll probably find it easier to use the Swift array type ([ContainedType]) than NSArray. Amongst other things, sorting Swift arrays is more straightforward than having to use NSSortDescriptor etc.
NSDates can be compared using .compare like so:
let d1 = NSDate()
let d2 = NSDate()
d1.compare(d2) == .OrderedAscending
But you might prefer them to implement the Comparable protocol, which can be done like this:
extension NSDate: Comparable { }
public func ==(lhs: NSDate, rhs: NSDate) -> Bool {
return lhs.isEqualToDate(rhs)
}
public func <(lhs: NSDate, rhs: NSDate) -> Bool {
return lhs.compare(rhs) == .OrderedAscending
}
// now you can do
d1 > d2
d1 <= d2 //etc
With this, you can sort arrays of dates:
let arr = [d2, d1]
let sortedArr = arr.sorted(<) // or > for descending order
If you had dictionaries of dates, you would need to fetch the date value out dictionary and compare it, but that’s easy:
let dicts = [["Date":d2], ["Date":d1]]
// pass sorted a function that compares two candidates in the array
dicts.sorted { lhs, rhs in
// even though dictionary lookups return optionals,
// this is fine because < is defined for optionals of Comparables
// (nil is < anything else)
lhs["Date"] < (rhs["Date"]
}
Except… chances aren’t that you actually have an array of [String:NSDate] – you probably have a bunch of different types in there and its actually a [String:AnyObject], so you need to convert the type:
// dictionaries contain various different kind of values
let dicts: [[String:AnyObject]] = [["Date":d2], ["Date":d1]]
dicts.sorted { lhs, rhs in
(lhs["Date"] as? NSDate) < (rhs["Date"] as? NSDate)
}
But that said, you probably at this point want to think about storing your data not as a dictionary of stuff, but as a proper data structure:
struct Post {
let timestamp: NSDate
// and various other properties...
}
let posts: [Post] = [
Post(timestamp: d1),
Post(timestamp: d2)
]
posts.sorted { $0.timestamp < $1.timestamp }
EDIT: your comment mentions that you actually have a dictionary of type [String:String]. This means you need to convert the strings to dates somehow before comparing them. In which case, you could use NSDateFormatter to convert as part of the comparison. Some example code:
let dicts: [[String:String]] = [["Date":"May 20 20015"], ["Date":"May 20 2014"]]
let fmt = NSDateFormatter()
fmt.dateStyle = .MediumStyle
let sortedDicts = dicts.sorted { lhs, rhs in
lhs["Date"].flatMap(fmt.dateFromString) < rhs["Date"].flatMap(fmt.dateFromString)
}
So, when doing the comparison, fetch the date string out, then convert it to a NSDate using the formatter, then compare the result.
Note, this uses flatMap – this is because dateFromString itself returns an optional, and you don’t want an optional-optional so you need to flatten the result.
The big downside to this is there’s no detection of invalid dates. If you got a date in there that was a bad format (say, month and day in the wrong order), dateFromString would return nil and it’d get sorted towards the start of the array.
The other downside of this is it’s getting pretty horribly inefficient. You may well be better off converting your data to more structured form (e.g. a struct with an NSDate-typed member variable) before doing this kind of processing.

Related

Avoid counting values of Ints with for loop in Kotlin

I have a list of A class objects
data class A{
val abc: Abc
val values: Int?
}
val list = List<A>
If I want to count how many objects I have in list I use:
val count= a.count()
or val count= a.count(it -> {})
How to append all values in the list of objects A avoiding for loop? Generaly Im looking for proper kotlin syntax with avoiding code below
if (a!= null) {
for (i in list) {
counter += i.values!!
}
}
Either use sumBy or sum in case you have a list of non-nullable numbers already available, i.e.:
val counter = list.sumBy { it.values ?: 0 }
// or
val counter = extractedNonNullValues.sum()
The latter only makes sense if you already mapped your A.values before to a list of non-nullable values, e.g. something like:
val extractedNonNullValues= list.mapNotNull { it.values } // set somewhere else before because you needed it...
If you do not need such an intermediate extractedNonNullValues-list then just go for the sumBy-variant.
I don't see you doing any appending to a list in the question. Based on your for loop I believe what you meant was "How do I sum properties of objects in my list". If that's the case you can use sumBy, the extension function on list that takes a labmda: ((T) -> Int) and returns an Int like so:
val sum = list.sumBy { a -> a.values ?: 0 }
Also, calling an Int property values is pretty confusing, I think it should be called value. The plural indicates a list...
On another note, there is a possible NPE in your original for loop. Avoid using !! on nullable values as, if the value is null, you will get an NPE. Instead, use null coalescing (aka elvis) operator to fall back to a default value ?: - this is perfectly acceptable in a sum function. If the iteration is not to do with summing, you may need to handle the null case differently.

Use Array<String> in a Flux

Could you guys help me out with using Array and stream (?) over it to use single element (String) to save Movie to db and return FLux. Spring specific stuff isn't important - just the way to iterate over alphabet and create random Movies. What's the best and most-kotlinish way of doing this?
val alphabet = arrayOf("A".."Z")
val exampleMovies: Flux<Movie> = Flux.just(alphabet)
.flatMap { movieRepository.save(Movie(name = it)) }
I'm getting compilation error:
Error:(15, 62) Kotlin: Type mismatch: inferred type is Array<ClosedRange<String>>! but String? was expected
The problem is that arrayOf("A".."Z") will give an Array<ClosedRange<String>>, i.e. the array has one element of type ClosedRange. What you actually wanted to have is an Array<String> with elements A, B, C, ..., Z I guess? Unfortunately, the range operator doesn't work like this for Strings, explained here.
Instead, create that array by mapping a CharRange accordingly:
val alphabet = ('A'..'Z').map(Char::toString).toTypedArray()

Stop Rounding with NSExpression in Calculator [duplicate]

I want to calculate a string, which I'm doing by this:
NSExpression *expression = [NSExpression expressionWithFormat:calculationString];
float result = [[expression expressionValueWithObject:nil context:nil] floatValue];
NSLog(#"%f", result);
The problem is, when calculationstring is 1/2, the result is 0. I tried to change float with double and NSNumber and the %f to %f and %#, but I always just get 0. What to I have to change?
Also if it matters, I am in Europe, so I have commas instead of points for this value, but it shouldn't matter as I am logging with %f which shows it as points. Just for information
Basically, you just need to tell it that you are performing floating point operation,
1.0/2
1.0/2.0
1/2.0
Will all work
Typing in NSExpression is much like in C: literals that look like integers (no decimal point/comma) are treated as integers and thus use integer division. (Under integer division, 1/2 is zero. If you want 0.5, you need floating point division.) This happens when the expression is parsed and evaluated, so attempting to change the type of the result or the formatting of the output has no effect -- those things happen after parsing and evaluation.
If your calculationString is entirely under your control, it's easy to make sure that you use floating point literals anywhere you want floating point division. (That is, use 1.0/2 instead of 1/2.) If not, you'll need to change it such that it does -- here it's probably better to decompose the parsed NSExpression and change an operand rather than munge the string.
Followup edit on the "decompose" bit: String munging in content that you know to have higher-order structure is generally problematic. And with NSExpression, you already have a parser (who's smarter than a simple regex) decomposing the string for you — that is in fact what NSExpression is all about.
So, if you're working with a user-provided string, don't try to change the expression by changing the string. Let NSExpression parse it, then use properties of the resulting object to pick it apart into its constituent expressions. If your string is simply "1/2", then your expression has an array of two arguments and the function "divide:by:" — you can replace it with an equivalent function where one of the arguments is explicitly a floating-point value:
extension NSExpression {
var floatifiedForDivisionIfNeeded: NSExpression {
if function == "divide:by:", let args = arguments, let last = args.last,
let firstValue = args.first?.constantValue as? NSNumber {
let newFirst = NSExpression(forConstantValue: firstValue.doubleValue)
return NSExpression(forFunction: function, arguments: [newFirst, last])
} else {
return self
}
}
}
I think You need to User DDMathParser Which is best in this situation. I have used it in One of my project which is facing same problem as you have faced
DDMathEvaluator *eval = [DDMathEvaluator defaultMathEvaluator];
id value=[eval evaluateString:#"1/2" withSubstitutions:nil error:&error];
NSLog(#"Result %#",value);
Result 0.5
Rickster's solution worked, but had problems with expressions like 5*5/2, where the first argument (here 5*5) was not just a number.
I found a different solution here that works for me: https://stackoverflow.com/a/46554342/6385925
for people who still have this problem i did a somewhat quick fix:
extension String {
var mathExpression: String {
var returnValue = ""
for value in newString.components(separatedBy: " ") {
if value.isOperator {
returnValue += value
} else {
returnValue += "\(Double(value) ?? 0)"
}
}
return returnValue
}
var isOperator: Bool {
["+", "-", "/", "x", "*"].contains(self)
}
}

How to return-7-6-5-4-3-2-1012345678910111213

Code below is in Objective C in Xcode. I am trying to return -7-6-5-4-3-2-1012345678910111213 as the method is expecting that response. number = -7 and otherNumber = 13 How do I return the series of numbers? I tried the method below but with no success...
while (number < otherNumber) {
++number;
return number;
}
Another thing to look out for is how your parameters are getting passed in to the method. Since we dont know if "number" is always going to be less than "otherNumber" you should check to find out which of the two numbers being passed in is lower before using them in your while loop.
this is very similar to the previous post but it might make it a tad clearer:
//find which number is low and which is high and set it accordingly
while (low <= high){
//then append low to end of string
++low;
}
//return your string
And this handles the case when the numbers are equal
In Objective-C, methods can only have one return value.
If your method returns an array, something like this would work:
// Create an NSMutableArray
while (number < otherNumber) {
// Add the number to the array
++number;
}
// Return the array
Or, similarly, if your method returns a string:
// Create an NSMutableString
while (number < otherNumber) {
// Append the number to the end of the string
++number;
}
// Return the string
A few notes:
your conditional, number < otherNumber, won't capture the case where number == otherNumber. Since in your example otherNumber is 13, and you want that included, you may want to use number <= otherNumber.
you can only compare scalar numbers (like NSInteger or CGFloat) with the inequality operators (like < and >). However, you can only add objects to NSMutableArray and NSMutableString. So you'll need to convert between the scalar numbers and NSNumber as appropriate.
Since it looks like you're learning Objective-C, note that this is different from Swift, which does allow methods to return multiple values.

LINQ and CASE Sensitivity

I have this LINQ Query:
TempRecordList = new ArrayList(TempRecordList.Cast<string>().OrderBy(s => s.Substring(9, 30)).ToArray());
It works great and performs sorting in a way that's accurate but a little different from what I want. Among the the result of the query I see something like this:
Palm-Bouter, Peter
Palmer-Johnson, Sean
Whereas what I really need is to have names sorted like this:
Palmer-Johnson, Sean
Palm-Bouter, Peter
Basically I want the '-' character to be treated as being lower than the character so that names that contain it show up later in an ascending search.
Here is another example. I get:
Dias, Reginald
DiBlackley, Anton
Instead of:
DiBlackley, Anton
Dias, Reginald
As you can see, again, the order is switched due to how the uppercase letter 'B' is treated.
So my question is, what do I need to change in my LINQ query to make it return results in the order I specified. Any feedback would be greatly appreaciated.
By the way, I tried using s.Substring(9, 30).ToLower() but that didn't help.
Thank you!
To customize the sorting order you will need to create a comparer class that implements IComparer<string> interface. The OrderBy() method takes comparer as second parameter.
internal sealed class NameComparer : IComparer<string> {
private static readonly NameComparer DefaultInstance = new NameComparer();
static NameComparer() { }
private NameComparer() { }
public static NameComparer Default {
get { return DefaultInstance; }
}
public int Compare(string x, string y) {
int length = Math.Min(x.Length, y.Length);
for (int i = 0; i < length; ++i) {
if (x[i] == y[i]) continue;
if (x[i] == '-') return 1;
if (y[i] == '-') return -1;
return x[i].CompareTo(y[i]);
}
return x.Length - y.Length;
}
}
This works at least with the following test cases:
var names = new[] {
"Palmer-Johnson, Sean",
"Palm-Bouter, Peter",
"Dias, Reginald",
"DiBlackley, Anton",
};
var sorted = names.OrderBy(name => name, NameComparer.Default).ToList();
// sorted:
// [0]: "DiBlackley, Anton"
// [1]: "Dias, Reginald"
// [2]: "Palmer-Johnson, Sean"
// [3]: "Palm-Bouter, Peter"
As already mentioned, the OrderBy() method takes a comparer as a second parameter.
For strings, you don't necessarily have to implement an IComparer<string>. You might be fine with System.StringComparer.CurrentCulture (or one of the others in System.StringComparer).
In your exact case, however, there is no built-in comparer which will handle also the - after letter sort order.
OrderBy() returns results in ascending order.
e comes before h, thus the first result (remember you're comparing on a substring that starts with the character in the 9th position...not the beginning of the string) and i comes before y, thus the second. Case sensitivity has nothing to do with it.
If you want results in descending order, you should use OrderByDescending():
TempRecordList.Cast<string>
.OrderByDescending(s => s.Substring(9, 30)).ToArray());
You might want to just implement a custom IComparer object that will give a custom priority to special, upper-case and lower-case characters.
http://msdn.microsoft.com/en-us/library/system.collections.icomparer.aspx

Resources