Comparing two arrays in swift, with different lengths - arrays

I want to compare two arrays with each other and append the value that is not in the other array to a new array. The problem now is that all of the values that does not equal the other array already get appended, but I want only the values that are new in the other array getting appended.
I hope that the problem is clear. Sorry if it's a very vague question. I try to be clear haha.
The code and output is printed below:
// Iterate through all possible values
for i in 0...messages.count-1{
var match = false
for r in 0...self.messages.count-1{
println("NIEUWE" + messages[i].getID() + "OUDE" + self.messages[r].getID())
if(messages[i].getID().toInt() == self.messages[r].getID().toInt()){
var match = true
println(match)
break
}
}
if (!match) {
newArray.append(messages[i])
println(newArray)
}
}
Output:
NIEUWE170OUDE170
NIEUWE170OUDE171
true
[PostDuif.Message]
NIEUWE171OUDE170
true
[PostDuif.Message, PostDuif.Message]
NIEUWE172OUDE170
true

This "I want to compare two arrays with each other and append the value that is not in the other array to a new array" is just 'set difference'
var s1 = Set(["a", "b", "c"]) // this to be similar to your need
var s2 = Set(["b", "c", "d"])
var s3 = s2.subtract (s1)
As such:
9> var s3 = s2.subtract(s1)
s3: Set<String> = {
[0] = "d"
}
Note that you have subtract, intersect, and union with inPlace options as methods on the Set type. New to Swift 1.2.

Related

Find objects that include an array that contains all elements of a second array

I'm trying to filter a set of objects based on values in one of their elements based on another array. I've got it working with filter just fine if the search is "OR" - it returns give me all objects for which at least one of the strings in the search array is found.
But I can't figure out how to make it work as an AND search - returning only the objects that match ALL of the strings in the search array.
Example:
struct Schedule {
let title: String
let classTypes: [String]
}
let schedule1 = Schedule(title: "One", classTypes: ["math","english","chemistry","drama"])
let schedule2 = Schedule(title: "Two", classTypes: ["pe","math","biology"])
let schedule3 = Schedule(title: "Three", classTypes: ["english","history","math","art"])
let schedules = [schedule1, schedule2, schedule3]
let searchArray = ["math", "english"]
//works for OR - "math" or "english"
var filteredSchedules = schedules.filter { $0.classTypes.contains(where: { searchArray.contains($0) }) }
I'd like to find a way for it to use the same search array
let searchArray = ["math", "english"]
But only return items 1 & 3 - as they both have BOTH math and english in the list.
There are good examples of AND conditions when the AND is across different search criteria: car type and colour - but I've been unable to find an example where the criteria are dynamically based on items in an array. For context, I could have dozens of schedules with 20+ class types.
You can work with a Set, isSubset will return true if the schedules element contains all elements of the searchSet
let searchSet = Set(searchArray)
var filteredSchedules = schedules.filter { searchSet.isSubset(of: $0.classTypes) }
As suggested by #LeoDabus it might be worth changing the type of classTypes to Set instead of arrays (if order doesn't matter) since they seems to be unique and then the filtering can be done in the opposite way without the need to convert searchArray each time
var filteredSchedules = schedules.filter { $0.classTypes.isSuperset(of: searchArray) }

How prevent Object.keys() sort?

The problem with the ECMA standard for sort of Object.keys() is known:
Object.keys() handle all keys with integer (example: 168), including integer as strings (example: "168"), as a integer. The result is, both are the same (168 === "168"), and overwrite itself.
var object = {};
object["168"] = 'x';
object[168] = 'y';
Object.keys(object); // Array [ "168" ]
object[Object.keys(object)]; // "y"
Interestingly, all keys (including pure integer keys) are returned as a string.
The ecma262 wrote about this: All keys will be handle as a integer, expect the key is a String but is not an array index.
https://tc39.es/ecma262/#sec-ordinaryownpropertykeys
That should tell us: 168 === "168". A toString() do not solve the problem.
var object = {};
object[[3].toString()] = 'z';
object[[1].toString()] = 'x';
object[[2].toString()] = 'y';
Object.keys(object);
// Array(3) [ "1", "2", "3" ]
Paradoxically, in this case, only integer apply as "enumerable" (it's ignoring array.sort(), that sort also strings with letters.).
My question about this is simple: How can i prevent the sort function in Object.keys()? I have testet the Object.defineProperties(object, 1, {value: "a", enumerable: true/false}), but that mean not realy enumerable in the case of integer or string or integer-like string. It means only should it be counted with or not. It means "counted" like omit (if it false), not "enumerabled" like ascending or descending.
A answere like that is not a good answer: Please use only letters [a-zA-Z] or leastwise a letter at the first position of keyword.
What I want: That the keys are not sorted, but output in the order in which they were entered, whether integer, string or symbol.
Disclaimer: Please solutions only in JavaScript.
Javascript Objects are unordered by their nature. If you need an ordered object-like variable I would suggest using a map.
To achieve what you're looking for with a map instead of object you'd do something like the below:
var map1 = new Map();
map1.set("123", "c");
map1.set(123, "b");
var iterator1 = map1.keys();
var myarray = [];
for (var i = 0; i < map1.size; i++) {
myarray.push(iterator1.next().value);
}
console.log(myarray);
// Array ["123", 123]
Unfortunately it's not compatible with IE and I'm not sure how else you could achieve what you need without it. A quick Google did return something about jQuery maps, though.
If you don't want to use jQuery and still need to support IE some points are below:
Is there anything stopping you using an array rather than JS object to store the data you need? This will retain the order per your requirements unlike objects. You could have an object entry in each iteration which represents the key then use a traditional foreach to obtain them as an array. I.e.
The array:
var test_array = [
{key: 123, value: 'a value here'},
{key: "123", value: 'another value here'}
];
// console.log(test_array);
Getting the keys:
var test_array_keys = [];
test_array.forEach(function(obj) { test_array_keys.push(obj['key']); } );
// console.log(test_array_keys);
Then if you needed to check whether the key exists before adding a new entry (to prevent duplicates) you could do:
function key_exists(key, array)
{
return array.indexOf(key) !== -1;
}
if(key_exists('12345', test_array_keys))
{
// won't get here, this is just for example
console.log('Key 12345 exists in array');
}
else if(key_exists('123', test_array_keys))
{
console.log('Key 123 exists in array');
}
Would that work? If not then the only other suggestion would be keeping a separate array alongside the object which tracks the keys and is updated when an entry is added or removed to/from the object.
Object Keys sorted and store in array
First Creating student Object. then sort by key in object,last keys to store in array
const student={tamil:100, english:55, sci:85,soc:57}
const sortobj =Object.fromEntries(Object.entries(student).sort())
console.log(Object.keys(sortobj))
use map instead of an object.
let map = new Map()
map.set("a", 5)
map.set("d", 6)
map.set("b", 12)
to sort the keys (for example, to update a chart data)
let newMap = new Map([...map.entries()].sort())
let keys = Array.from(newMap.keys()) // ['a','b','d']
let values = Array.from(newMap.values()) // [5,12,6]

Kotlin - Find matching objects in array

Let's say I have an array of strings and I want to get a list with objects that match, such as:
var locales=Locale.getAvailableLocales()
val filtered = locales.filter { l-> l.language=="en" }
except, instead of a single value I want to compare it with another list, like:
val lang = listOf("en", "fr", "es")
How do I do that? I'm looking for a one-liner solution without any loops. Thanks!
Like this
var locales = Locale.getAvailableLocales()
val filtered = locales.filter { l -> lang.contains(l.language)}
As pointed out in comments, you can skip naming the parameter to the lambda, and use it keyword to have either of the following:
val filtered1 = locales.filter{ lang.contains(it.language) }
val filtered2 = locales.filter{ it.language in lang }
Just remember to have a suitable data structure for the languages, so that the contains() method has low time complexity like a Set.

swift - using .map on struct array

i have a struct array that i want "break up" into smaller arrays that can be called as needed or at least figure out how i can map the items needed off one text value.
the struct:
struct CollectionStruct {
var name : String
var description : String
var title : String
var image : PFFile
var id: String
}
and the array made from the struct
var collectionArray = [CollectionStruct]()
var i = 0
for item in collectionArray {
print(collectionArray[i].name)
i += 1
}
printing partArray[i].name gives the following result:
pk00_pt01
pk00_pt02
pk00_pt03
pk01_pt01
pk01_pt02
pk01_pt03
pk01_pt04
pk01_pt05
pk01_pt06
pk01_pt07
pk01_pt08
this is just some test values but there could be thousands of entries here so i wanted to filter the entire array just by the first 4 characters of [i].name i can achieve this by looping through as above but is this achievable using something like .map?
I wanted to filter the entire array just by the first 4 characters of
[i].name
You can achieve this by filtering the array based on the substring value of the name, as follows:
let filteredArray = collectionArray.filter {
$0.name.substring(to: $0.name.index($0.name.startIndex, offsetBy: 4)).lowercased() == "pk00"
// or instead of "pk00", add the first 4 characters you want to compare
}
filteredArray will be filled based on what is the compared string.
Hope this helped.
If you want to group all data automatically by their name prefix. You could use a reducer to generate a dictionary of grouped items. Something like this:
let groupedData = array.reduce([String: [String]]()) { (dictionary, myStruct) in
let grouper = myStruct.name.substring(to: myStruct.name.index(myStruct.name.startIndex, offsetBy: 4))
var newDictionart = dictionary
if let collectionStructs = newDictionart[grouper] {
newDictionart[grouper] = collectionStructs + [myStruct.name]
} else {
newDictionart[grouper] = [myStruct.name]
}
return newDictionart
}
This will produce a dictionary like this:
[
"pk00": ["pk00_pt01", "pk00_pt02", "pk00_pt03"],
"pk01": ["pk01_pt01", "pk01_pt02", "pk01_pt03", "pk01_pt04", "pk01_pt05", "pk01_pt06", "pk01_pt07"],
"pk02": ["pk02_pt08"]
]
Not sure if i am understanding you correctly but it sounds like you are looking for this...
To create a new array named partArray from an already existing array named collectionArray (that is of type CollectionStruct) you would do...
var partArray = collectionArray.map{$0.name}

Sort array in Swift Ascending with empty items

In Xcode (Swift) I have an array that is initialized to 100 empty items:
var persons = [String](count:100, repeatedValue: "")
With some functions I add content to the places in the array, starting at 0.
So for example my array is at some given moment:
["Bert", "Daniel", "Claire", "Aaron", "", "", ... ""]
With the dots representing the rest of the empty items. I use this function for sorting my array alphabetically:
persons = persons.sorted {$0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }
This gives me an array back like this:
["", "", ... , "Aaron", "Bert", "Claire", "Daniel"]
What I want is to sort my array alphabetically but not with the empty items at the front. I need to get an array back like:
["Aaron", "Bert", "Claire", "Daniel", "", "", ... , ""]
For my part, I do not want an array with empty items but I found I couldn't add a value to my array if I did not declare like a 100 items (the array won't be filled to a 100 items, that's for sure).
Can anyone help me out?
As #Antonio said, it looks like you need a descending order set of strings. Besides the Dictionary method in #Antonio's answer (which works great), you can also use NSMutableSet (bridged from Objective-C):
let personSet = NSMutableSet()
personSet.addObject("Aaron")
personSet.addObject("Daniel")
personSet.addObject("Claire")
personSet.addObject("Aaron")
personSet.addObject("Bert")
personSet.addObject("Bert")
personSet.addObject("Joe")
personSet.removeObject("Joe") // You can remove too of course
Which creates the set:
{(
Claire,
Aaron,
Daniel,
Bert
)}
Then, when you want the people as an Array, you can use the allObjects cast to a [String]:
personSet.allObjects as [String]
And you can sort it just like you're currently doing:
let people = (personSet.allObjects as [String]).sort {$0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }
Which makes people:
[Aaron, Bert, Claire, Daniel]
For those wondering how to sort the Array as originally stated in the question (Ascending but with empty strings at the end), that can be done with a little bit of custom logic in the sort function:
var persons = ["Bert", "Daniel", "Claire", "Aaron", "", "", ""]
persons.sort { (a, b) -> Bool in
if a.isEmpty {
return false
} else if b.isEmpty {
return true
} else {
return a.localizedCaseInsensitiveCompare(b) == .OrderedAscending
}
}
Result:
["Aaron", "Bert", "Claire", "Daniel", "", "", ""]
Reading comments in your question and other answers, I realize that you need a ordered set, containing unique values. There's no built in data structure in swift for that, but it can be easily be done by using a dictionary: simply use the string value as dictionary key, and a boolean as dictionary value - this ensures that keys are unique:
var persons = [String : Bool]()
persons["Bert"] = true
persons["Daniel"] = true
persons["Clair"] = true
persons["Clair"] = true
persons["Aaron"] = true
persons["Daniel"] = true
persons["Clair"] = true
You can quickly verify that with the above code the dictionary contains 4 elements only.
Next, obtain a copy of the dictionary keys (as an array):
var values = persons.keys.array
and sort it:
values.sort { $0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }
Alternatively, if you want to stick with the fixed sized array, you can remove the empty items before sorting:
persons = persons
.filter( { $0.isEmpty == false } )
.sorted {$0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }
I think you are confused about arrays. Swift arrays are not statically allocated structures which must be allocated and filled to maximum design capacity. Below is a crude example of how you can accomplish most of what you are expressing here. However, I really think that a dictionary is better suited to your needs.
var persons = [String]()
var inputData = ["Bert", "Daniel", "Bert", "Claire", "Aaron"]
for item in inputData {
var found = false
for existing in persons {
if existing == item {
found = true
break
}
}
if (!found) {
persons.append(item)
}
}
persons.sort{$0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }
println(persons)

Resources