Sort the array with specific character in swift iOS [duplicate] - arrays

Given is the following:
var theArray: [String] = ["uncool", "chill", "nifty", "precooled", "dandy", "cool"]
I want to sort the array by how similar the words are to the key word.
var keyWord: String = "cool"
The wanted result would be:
print// ["cool", "uncool", "precooled", ...] and then it does not matter anymore. But the words that are the key or contain it should be the very first objects.
My closest tryout so far has been:
let _theArray = entries.sorted { element1, element2 in
return element1.contains(keyWord) && !element2.contains(keyWord)
}
But that results in uncool being the first item, then precooled and the most related item cool even comes after nifty .
What am I missing?

You can define your own similarity sorting method. Note that I have also added a hasPrefix priority over the ones which only contains the keyword which you can just remove if you don't want it:
var theArray = ["chill", "nifty", "precooled", "cooldaddy", "cool", "coolguy", "dandy", "uncool"]
let key = "cool"
let sorted = theArray.sorted {
if $0 == key && $1 != key {
return true
}
else if $0.hasPrefix(key) && !$1.hasPrefix(key) {
return true
}
else if !$0.hasPrefix(key) && $1.hasPrefix(key) {
return false
}
else if $0.hasPrefix(key) && $1.hasPrefix(key)
&& $0.count < $1.count {
return true
}
else if $0.contains(key) && !$1.contains(key) {
return true
}
else if !$0.contains(key) && $1.contains(key) {
return false
}
else if $0.contains(key) && $1.contains(key)
&& $0.count < $1.count {
return true
}
return false
}
print(sorted) // ["cool", "coolguy", "cooldaddy", "uncool", "precooled", "chill", "nifty", "dandy"]
You can also extend Sequence and create a sorted by key similarity method:
extension Sequence where Element: StringProtocol {
func sorted<S>(by key: S) -> [Element] where S: StringProtocol {
sorted {
if $0 == key && $1 != key {
return true
}
else if $0.hasPrefix(key) && !$1.hasPrefix(key) {
return true
}
else if !$0.hasPrefix(key) && $1.hasPrefix(key) {
return false
}
else if $0.hasPrefix(key) && $1.hasPrefix(key)
&& $0.count < $1.count {
return true
}
else if $0.contains(key) && !$1.contains(key) {
return true
}
else if !$0.contains(key) && $1.contains(key) {
return false
}
else if $0.contains(key) && $1.contains(key)
&& $0.count < $1.count {
return true
}
return false
}
}
}
let sorted = theArray.sorted(by: key) // "cool", "coolguy", "cooldaddy", "uncool", "precooled", "chill", "nifty", "dandy"]
And the mutating version as well:
extension MutableCollection where Element: StringProtocol, Self: RandomAccessCollection {
mutating func sort<S>(by key: S) where S: StringProtocol {
sort {
if $0 == key && $1 != key {
return true
}
else if $0.hasPrefix(key) && !$1.hasPrefix(key) {
return true
}
else if !$0.hasPrefix(key) && $1.hasPrefix(key) {
return false
}
else if $0.hasPrefix(key) && $1.hasPrefix(key)
&& $0.count < $1.count {
return true
}
else if $0.contains(key) && !$1.contains(key) {
return true
}
else if !$0.contains(key) && $1.contains(key) {
return false
}
else if $0.contains(key) && $1.contains(key)
&& $0.count < $1.count {
return true
}
return false
}
}
}

First you need a measure of how similar two strings are. Here's a simple example:
extension String {
func equalityScore(with string: String) -> Double {
if self == string {
return 2 // the greatest equality score this method can give
} else if self.contains(string) {
return 1 + 1 / Double(self.count - string.count) // contains our term, so the score will be between 1 and 2, depending on number of letters.
} else {
// you could of course have other criteria, like string.contains(self)
return 1 / Double(abs(self.count - string.count))
}
}
}
Once you have that, you can use it to sort the array:
var theArray: [String] = ["uncool", "chill", "nifty", "precooled", "dandy", "cool"]
var compareString = "cool"
theArray.sort { lhs, rhs in
return lhs.equalityScore(with: compareString) > rhs.equalityScore(with: compareString)
}
Result: ["cool", "uncool", "precooled", "chill", "nifty", "dandy"]

Related

Why is it saying the index is out of range?

It is saying "error, index out of range"
I already tried making it a 0..< that still returns the error. Been looking over the code for 30 minutes can't figure out what I messed up
func kidsWithCandies(_ candies: [Int], _ extraCandies: Int) -> [Bool] {
var greatestCandyNum = 0
var arrayOfBools = Array<Bool>()
for kid in candies {
if candies[kid] > greatestCandyNum {
greatestCandyNum = candies[kid]
arrayOfBools.append(false)
}
}
for kid in candies {
if candies[kid] + extraCandies >= greatestCandyNum{
arrayOfBools[kid] = true
}
}
for kid in candies {
if candies[kid] == greatestCandyNum {
arrayOfBools[kid] = true
} else if candies[kid] > greatestCandyNum {
greatestCandyNum = candies[kid]
arrayOfBools[kid] = true
}
}
return arrayOfBools
}
So the main issue is that you cannot pass the value from the for loop into the index of the array. You can use the enumerate function to generate the index of where you are in the array position.
As seen with:
for (index, kid) in candies.enumerated()
I also fixed your initial for loop that would not generate a consistent Bool array if your candies array was not ascending. This was done with an else
Also please see that when iterating you can use the "kid" value rather than trying to find it again in the array.
Disclaimer: There are however much better ways of doing this.
func kidsWithCandies(_ candies: [Int], _ extraCandies: Int) -> [Bool] {
var greatestCandyNum = 0
var arrayOfBools = Array<Bool>()
for kid in candies {
if kid > greatestCandyNum {
greatestCandyNum = kid
arrayOfBools.append(false)
} else {
arrayOfBools.append(true)
}
}
for (index, kid) in candies.enumerated() {
if kid + extraCandies >= greatestCandyNum {
arrayOfBools[index] = true
}
}
for (index, kid) in candies.enumerated() {
if kid == greatestCandyNum {
arrayOfBools[index] = true
} else if kid > greatestCandyNum {
greatestCandyNum = kid
arrayOfBools[index] = true
}
}
return arrayOfBools
}

I cannot set while to work properly in for loops in ts

I want to get array of arrays with unique values, but at some point
while
gets missed or ignored (I presume because of the asynchronous nature of it). Can someone help with a adding a promise to this, or setting
async/await
structure or giving better advise how to check the arrays. I tried adding
async/await
but I get error and I am not sure where I can add a promise or use it. It is
getSeveralArrays() {
for (let index = 0; index < 50; index++) {
this.getValue();
}
}
getValue() {
this.i++;
this.array = [];
this.randomArray = [];
for (let index = 0; index < 4; index++) {
this.randomValue = this.getRandom();
if (this.array.length === 2) {
while ((this.array[0] === this.randomValue) || (this.array[1] === this.randomValue)) {
this.randomValue = this.getRandom();
}
this.array.push(this.randomValue);
} else if (this.array.length === 3) {
while ((this.array[0] === this.randomValue) || (this.array[1] === this.randomValue) || (this.array[2] === this.randomValue)) {
this.randomValue = this.getRandom();
}
this.array.push(this.randomValue);
} else {
this.array.push(this.randomValue);
}
console.log({...this.array});
this.randomArray.push({ind: this.i, val: this.array});
}
}
getRandom() {
const value = Math.floor(Math.random() * 4);
return value;
}
There is nothing asynchronous in all your code, so no need for async/await. Also, while is not asynchronous.
You were missing a case for the length of 1 though, so the second element could always be the same as the first.
class X {
getSeveralArrays() {
for (let index = 0; index < 50; index++) {
this.getValue();
}
}
getValue() {
this.i++;
this.array = [];
this.randomArray = [];
for (let index = 0; index < 4; index++) {
this.randomValue = this.getRandom();
if (this.array.length === 1) {
while (this.array[0] === this.randomValue) {
this.randomValue = this.getRandom();
}
this.array.push(this.randomValue);
} else if (this.array.length === 2) {
while (this.array[0] === this.randomValue || this.array[1] === this.randomValue) {
this.randomValue = this.getRandom();
}
this.array.push(this.randomValue);
} else if (this.array.length === 3) {
while (
this.array[0] === this.randomValue ||
this.array[1] === this.randomValue ||
this.array[2] === this.randomValue
) {
this.randomValue = this.getRandom();
}
this.array.push(this.randomValue);
} else {
this.array.push(this.randomValue);
}
console.log({ ...this.array });
this.randomArray.push({ ind: this.i, val: this.array });
}
}
getRandom() {
const value = Math.floor(Math.random() * 4);
return value;
}
}
console.log(new X().getSeveralArrays());
Also, all those checks can be simplified:
while (this.array.some(value => value === this.randomValue)) {
this.randomValue = this.getRandom();
}
And generally, just as a small code review: If you do not need to access something from another class method (or somewhere outside), you should not put all those values into class properties, but keep them as values inside your functions.
So this.randomValue and this.array should just be defined within your function with let or const.

Equivalent of angular.equals in angular2

I am working on migration of angular 1 project to angular 2 . In angular 1 project I was using angular.equals for object comparison angular.equals($ctrl.obj1, $ctrl.newObj); , I searched online for equivalent method in angular 2 but could not find any matching result.
#Günter Yes you are right there is no equivalent in angular2 . While searching more I found third party library lodash which will do same job as angular.equals and syntax is same as angular one and this library solves my problem
Code example from lodash documentation
var object = { 'a': 1 };
var other = { 'a': 1 };
_.isEqual(object, other);
// => true
object === other;
// => false
I rewrote Ariels answer (thank you!) to be TSLINT-friendly. You can also save some continues by using else if, but I think this is more clear. Maybe someone else needs it too:
export function deepEquals(x, y) {
if (x === y) {
return true; // if both x and y are null or undefined and exactly the same
} else if (!(x instanceof Object) || !(y instanceof Object)) {
return false; // if they are not strictly equal, they both need to be Objects
} else if (x.constructor !== y.constructor) {
// they must have the exact same prototype chain, the closest we can do is
// test their constructor.
return false;
} else {
for (const p in x) {
if (!x.hasOwnProperty(p)) {
continue; // other properties were tested using x.constructor === y.constructor
}
if (!y.hasOwnProperty(p)) {
return false; // allows to compare x[ p ] and y[ p ] when set to undefined
}
if (x[p] === y[p]) {
continue; // if they have the same strict value or identity then they are equal
}
if (typeof (x[p]) !== 'object') {
return false; // Numbers, Strings, Functions, Booleans must be strictly equal
}
if (!deepEquals(x[p], y[p])) {
return false;
}
}
for (const p in y) {
if (y.hasOwnProperty(p) && !x.hasOwnProperty(p)) {
return false;
}
}
return true;
}
}
Instead of writing a function to iterate through the objects, you could just use JSON.stringify and compare the two strings?
Example:
var obj1 = {
title: 'title1',
tags: []
}
var obj2 = {
title: 'title1',
tags: ['r']
}
console.log(JSON.stringify(obj1));
console.log(JSON.stringify(obj2));
console.log(JSON.stringify(obj1) === JSON.stringify(obj2));
In Angular 2 you should use pure JavaScript/TypeScript for that so you can add this method to some service
private static equals(x, y) {
if (x === y)
return true;
// if both x and y are null or undefined and exactly the same
if (!(x instanceof Object) || !(y instanceof Object))
return false;
// if they are not strictly equal, they both need to be Objects
if (x.constructor !== y.constructor)
return false;
// they must have the exact same prototype chain, the closest we can do is
// test there constructor.
let p;
for (p in x) {
if (!x.hasOwnProperty(p))
continue;
// other properties were tested using x.constructor === y.constructor
if (!y.hasOwnProperty(p))
return false;
// allows to compare x[ p ] and y[ p ] when set to undefined
if (x[p] === y[p])
continue;
// if they have the same strict value or identity then they are equal
if (typeof (x[p]) !== "object")
return false;
// Numbers, Strings, Functions, Booleans must be strictly equal
if (!RXBox.equals(x[p], y[p]))
return false;
}
for (p in y) {
if (y.hasOwnProperty(p) && !x.hasOwnProperty(p))
return false;
}
return true;
}
You could just copy the original source code from angularjs for the angular.equals function. Usage: equals(obj1, obj2);
var toString = Object.prototype.toString;
function isDefined(value) {return typeof value !== 'undefined';}
function isFunction(value) {return typeof value === 'function';}
function createMap() {
return Object.create(null);
}
function isWindow(obj) {
return obj && obj.window === obj;
}
function isScope(obj) {
return obj && obj.$evalAsync && obj.$watch;
}
function isRegExp(value) {
return toString.call(value) === '[object RegExp]';
}
function simpleCompare(a, b) { return a === b || (a !== a && b !== b); }
function isDate(value) {
return toString.call(value) === '[object Date]';
}
function isArray(arr) {
return Array.isArray(arr) || arr instanceof Array;
}
function equals(o1, o2) {
if (o1 === o2) return true;
if (o1 === null || o2 === null) return false;
// eslint-disable-next-line no-self-compare
if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
if (t1 === t2 && t1 === 'object') {
if (isArray(o1)) {
if (!isArray(o2)) return false;
if ((length = o1.length) === o2.length) {
for (key = 0; key < length; key++) {
if (!equals(o1[key], o2[key])) return false;
}
return true;
}
} else if (isDate(o1)) {
if (!isDate(o2)) return false;
return simpleCompare(o1.getTime(), o2.getTime());
} else if (isRegExp(o1)) {
if (!isRegExp(o2)) return false;
return o1.toString() === o2.toString();
} else {
if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
keySet = createMap();
for (key in o1) {
if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
if (!equals(o1[key], o2[key])) return false;
keySet[key] = true;
}
for (key in o2) {
if (!(key in keySet) &&
key.charAt(0) !== '$' &&
isDefined(o2[key]) &&
!isFunction(o2[key])) return false;
}
return true;
}
}
return false;
}
a = { name: 'me' }
b = { name: 'me' }
a == b // false
a === b // false
JSON.stringify(a) == JSON.stringify(b) // true
JSON.stringify(a) === JSON.stringify(b) // true

how to check if object is an array of objects

I have a object like below which is an array of objects.
In swift language, How can i check whether object is an array of objects ?
DefinitionList = (
{
accountNum = {
isEditable = 1;
isRequired = 1;
};
bAccountType = {
isEditable = 1;
isRequired = 0;
},
},
{
accountNum = {
isEditable = 1;
isRequired = 1;
};
bAccountType = {
isEditable = 1;
isRequired = 0;
};
},
..
..)
Usually i use this in Swift 2 :
var DefinitionList = NSObject?()
DefinitionList = ["ciao" : "ciao"]
// DefinitionList = ["ciao"]
guard DefinitionList != nil else {
print("DefinitionList empty")
return
}
guard ((DefinitionList as? Array<NSObject>) != nil) else {
print("I'm a Dictionary")
return
}
print("I'm a Array")
Swift 3
var DefinitionList : NSObject?
// DefinitionList = ["ciao" : "ciao"] as NSObject
DefinitionList = ["ciao"] as NSObject
guard DefinitionList != nil else {
print("DefinitionList empty")
return
}
guard ((DefinitionList as? Array<NSObject>) != nil) else {
print("I'm a Dictionary")
return
}
print("I'm a Array")
You can use "is" operator in Swift language.
if objects is [AnyObject] {
print("right, its array of objects!")
} else {
print("no, its not an array of objects!")
}
Hope this will help you

Typescript sorting array with objects on multiple properties

I like to sort an array with objects that have multiple properties. My objects have a string called name and a boolean called mandatory.
First i want to sort on age, next on the name.
How do I do this?
Ordering by age is easy...:
this.model.mylist.sort((obj1: IObj, obj2: IObj => {
if (obj1.age < obj2.age) {
return -1;
}
if (obj1.age > obj2.age) {
return 1;
}
return 0;
});
Well, you only add comparation for case when the two age values are the same. So something like this should work:
this.model.mylist.sort((obj1: IObj, obj2: IObj) => {
if (obj1.age < obj2.age) {
return -1;
}
if (obj1.age > obj2.age) {
return 1;
}
return obj1.name.localeCompare(obj2.name);
});
Something like this should work. The method compares the current and next values and adds comparison to the case when the two age values are the same. Then assume the column name in order based on age.
private compareTo(val1, val2, typeField) {
let result = 0;
if (typeField == "ftDate") {
result = val1 - val2;
} else {
if (val1 < val2) {
result = - 1;
} else if (val1 > val2) {
result = 1;
} else {
result = 0;
}
}
return result;
}
-
this.model.mylist.sort((a, b) => {
let cols = ["age", "name"];
let i = 0, result = 0, resultordem = 0;
while (result === 0 && i < cols.length) {
let col = cols[i];
let valcol1 = a[col];
let valcol2 = b[col];
let typeField = "string";
if (col == "age") {
typeField = "number";
}
if (valcol1 != "null" && valcol1 != "null") {
resultordem = this.compareTo(valcol1, valcol2, typeField);
if (resultordem != 0) {
break;
}
}
i++;
}
return resultordem;
});

Resources