Finding the difference between two arrays in FSharp - f#

I have two arrays where I want to find the elements from the second array that are not in the first array.
I wrote the following code:
let array0 = [|"A";"B";"C"|]
let array1 = [|"B";"D";"E"|]
let inZero letter =
array0 |> Array.tryFind(fun l -> if l = letter then true else false)
array1|> Array.filter(fun l -> inZero(l).IsSome)
But I am wondering if there is something that is more idiomatic to FSharp.
Thanks in advance

If you do not care about duplicates, then you could write this using F# sets:
// Elements that are in array1, but not in array0
set array1 - set array0
// Elements that are in both arrays (which is what your sample returns)
Set.intersect (set array1) (set array0)
In both cases, you get back a new set<int>, which is also a seq<int>, so you can iterate over it or turn it back into array using Array.ofSeq.
If you want to keep duplicates (if there are duplicates in array1, then return the element multiple times), then I think what you have is good. If you wanted to compare multiple arrays against one, then it would make sense to turn array0 into a dictionary for more efficient lookup:
let inZero =
let set0 = set array0
set0.Contains
// Elements from array1 that are also in array0 (returns multiple
// copies of the same element if it appears repeatedly in array1)
array1 |> Array.filter inZero
The conversion to set has some cost, but it reduces the lookup time. So, depending on how you use this & what is the size of the arrays, this will have different performance characteristics. But the code looks nicer, so this would be my default choice.

Related

(Swift) moving objects while iterating over array

I am trying to remove some objects from 1 array, and move them to another.
I am doing this by removing them from a reversed array, and adding them to another array, like so:
var array1 = [1,2,3,4,5,6]
var array2 = [1,2]
for (index, number) in array1.enumerated().reversed() {
if(number>2) {
array1.remove(at: index)
array2.append(number)
}
}
The problem is, the objects in array 2 are obviously reversed (1,2,6,5,4,3)
I can easily come up with complicated workarounds, but I was wondering if there are any straightforward ways of doing this.
Thanks in advance!
Rather than appending the numbers insert them
array2.insert(number, at: 2)
You can do the same thing without a loop
let droppedItems = array1.dropFirst(2)
array1.removeLast(array1.count - 2)
array2.append(contentsOf: droppedItems)
If I understand you correctly, you want to move numbers from array1 to array2 if they are higher than 2:
// get only numbers higher than 2 and append them to the second array
array2.append(contentsOf: array1.filter { $0 > 2 })
// filter the moved items from the first array
array1 = array1.filter { $0 <= 2 }
or
// split the array into two parts in place
let index = array1.partition { $0 > 2 }
// move the second part
array2 += array1[index...]
// remove the second part
array1.removeSubrange(index...)
Reverse it, grab subarray then append to array2. You don't need to mutate array1. Something like:
array2.append(contentsOf: array1.reversed()[0..<array1.count-1])

How to Make a Struct Version of the NSCountedSet Class?

I am developing an app in which I need to take two arrays, both holding the same custom data type, MenuItem.
I need to compare them and find the common elements of both arrays.
I compare the two arrays by converting them both to sets and then using the compare function of the set struct.
However, I then need to convert the array of common elements to a Counted Set so that I can find how many of each element exist in the group of common elements.
The problem I am having is with converting the the array of common elements to a Counted Set. In Swift, there is an NSCountedSet class but no CountedSet struct like there are for most built-in swift Data Types:
let groups = Array(self.menu.keys)
//menu and cart are dictionaries the structure [MenuGroup: [MenuItem]]
let cartGroups = Array(self.cart.keys)
let group = groups[indexPath.section]
if cartGroups.contains(group) {
let items = menu[group] as [MenuItem]!
let cartItems = cart[group]
let itemsSet = Set<MenuItem>(items!)
let cartItemsSet = Set<MenuItem>(cartItems!)
let similarSet = itemsSet.intersection(cartItemsSet)
let similarArray = NSMutableArray(Array(similarSet))
let similarCounterSet = NSCountedSet(array: similarArray)
}
I want to create a struct based off the NSCountedSet class so that I can convert the array of common elements to a counted set.
The if statement is for something else, it does not pertain to this question.
Any thoughts on how to do this? If anybody has a different approach to this problem feel free to mention that too.
Thanks,
Simon
Thanks to Martin R I Found An Example of How to do This:
There are various CountedSet implementation available, e.g. https://github.com/0x7fffffff/CountedSet.

Multidimensional Array Looping in cellForRowAtIndexPath Swift

I have a multidimensional array that I want to display the values of onto one UILabel in each respective cell.
My arrays look like this:
var arrayExample = [["beverages", "food", "suppliers"]["other stuff, "medicine"]]
I'm looping through these values in the cellForRowAtIndexPath in order for it to display on different cells (on a UILabel) the appropriate values:
if let onTheLabel: AnyObject = arrayOfContactsFound as? AnyObject {
for var i = 0; i < objects!.count; i++ {
cell?.contactsUserHas.text = "\(onTheLabel[indexPath.row][i])" as! String
print("arrayOfContactsFound Printing! \(onTheLabel)")
}
}
When printing to the console I get:
arrayOfContactsFound Printing! (
(
beverages,
"supply chain",
pharmacuticals
)
)
But on my label I get "beverages". That's it. How can I get the other 2 values (or X amount if there are more or less than 3 values)?
My for in loop is obviously not doing the trick. Assuming I can optimize/fix that to display all the values?
Thanks in advance.
In your loop you're setting the text of your label multiple times. Each time you set it it doesn't accumulate, it completely replaces the current text with the new text. You'll want something like this:
// Remove the cast and the loop in your code example, and replace with this
let items = arrayOfContactsFound[indexPath.row]
let itemsString = items.joinWithSeparator(" ")
cell?.contactsUserHas.text = itemsString
Another thing to note is your cast doesn't quite make a lot of sense.
var arrayExample = [["beverages", "food", "suppliers"]["other stuff, "medicine"]]
So arrayExample is of type [[String]]. I'm assuming each cell in your table view represents one of the arrays of strings in your array. So each cell represents one [String]. So your items should be arrayExample[indexPath.row]. The cast to AnyObject doesn't make too much sense. If anything you'd be casting it to [[AnyObject]], but there's no reason to because the compiler should already know it's [[String]].

Use for loop to subtract two arrays of objects in Swift or is there a better way?

We need to subtract two arrays of CGPoint objects using Swift. Specifically, we want to find all the CGPoint objects in array A but not in array B.
Should we just loop through and manually compare elements, or is there a preferred mechanism native to Swift for doing this?
Some combination of filter and contains would do it:
let x = a.filter { !contains(b, $0) }
This is assuming there are no other characteristics of a and b you might exploit, such as them both being ordered (in which case it would be more efficient to walk them both in parallel).
In swift 2, this has changed to:
let x = a.filter { !b.contains($0) }

Swift: how do I concatenate a [[String]] to a [String] the right way?

There are new Array functions in swift - map, reduce etc.
I'd like to use these to concatenate a [[String]] to a [String] but I can't figure out how. (I'm assuming map or reduce would do what I want but could be wrong).
What is the best way to do what I need to?
You'll want to use reduce for that, since you're basically going from an array of T down to T, it's just that T = [String].
let stringArray = stringArrayArray.reduce([]) { $0 + $1 }
You can write it even more concisely by using operator shorthand instead of a closure:
let stringArray = stringArrayArray.reduce([], +)
And here's the full way to write it out, so you can see what's happening:
1: let stringArray = stringArrayArray.reduce([]) {
2: (accumulated: [String], element: [String]) -> [String] in
3: return accumulated + element
4: }
In line 1, we provide an empty array for the initial value. Line 2 defines the arguments and return type of the closure: accumulated is the initial value in the first iteration, and the result of the previous iteration thereafter, while element is the current element of the source array. Line 3 simply adds the accumulated array with the current element to merge them together.

Resources