Use Lodash to produce new object from existing object based on array of keys - filtering

I am relatively new to Lodash and I'm trying to do something that I'm sure is really easy.
I'm wondering which existing Lodash method to use to take an array of keys and an object, then return a new object containing only the properties (from the original object) that match the keys that were passed in.
let _ = require("lodash");
// DOES THE FUNCTION BELOW ALREADY EXIST?
let only = function (wanted, srcList) {
return _.reduce(wanted, function(out,key){
if(_.has(srcList, key)){
out[key] = srcList[key]
}
return out;
},{})
}
let src = {
w: "This is doble vuu",
x: "This is X",
y: "This is why?",
z: "This is zeee!"
};
var want = ['x','z'];
let newObject = only(want,src);
console.log(newObject)
The code I have works, but I don't want to re-invent the proverbial wheel, no matter how simple it is.

_.pick works perfectly as suggested by Gruff Bunny in the comments. I don't know why it didn't seem more obvious when I was going through dozens of methods available in Lodash

I believe that you want _.map.
It will return an object with the specified keys if you pass an array of strings as the second argument.

Related

func removing(Element) -> [T] {} on collection in swift

If you have a collection type in Swift (like a Set<T>) then you can remove something from the set like...
var s = // some set
s.remove(someElement)
and it will mutate s and remove the someElement from it.
However, this is a mutating function.
Is there a non-mutating function that would return a new set? Like...
let smallerSet = largerSet.removing(someElement)
I could use filter but then it turns this from a O(1) into an O(n) time problem.
If there isn't one already I can write one myself. Is there a convention for the name of a non-mutating function like this?
You can use subtracting function of the Set. For that you need to put that element to be deleted in another Set.
let elementToBeDeleted = Set(arrayLiteral: 1)
var wholeSet = Set(arrayLiteral: 1,2,3)
wholeSet = wholeSet.subtracting(element)
print(wholeSet) //This would print [2,3].
Hope this what you need to get things done.

How to use a value type object as a reference type?

In my model, I have some arrays:
var thisArray = [Object]
var thatArray = [Object]
var anotherArray = [Object]
In my view controller, I want to switch on a value to determine which array I will append to:
var whichArray: [Object]!
switch someValue {
case .thisArray: whichArray = thisArray
case .thatArray: whichArray = thatArray // "He went thatArray!"
case .anotherArray: whichArray = anotherArray
}
whichArray.append(object)
But of course this won't work because Array is a value type.
Is there a way to do this? Of course I could do the following:
switch someValue {
case .thisArray: thisArray.append(object)
case .thatArray: thatArray.append(object)
case .anotherArray: anotherArray.append(object)
}
But that is so inelegant and redundant! And if there's other more complex things going on in the surrounding code, then it's especially so.
Is there a solution here? Is it possible to create a reference to a value type?
PS. Even better, though really its own question, is if I could use the name of the case (e.g., "thisArray" for someValue = .thisArray) to set the array, by name (i.e., avoid the whole switch statement and just say objectName.append(object) or something like that) but as far as I know this isn't a thing. Or maybe this IS possible? And maybe it's my birthday?
Since Arrays are value types - as you have said yourself - they can't be passed around (or assigned) as a reference. One solution would be to create a wrapper class for the Array which itself would then be a reference type. You can then assign this wrapper class instead of the arrays themselves.
Now, given that you also said you might prefer to access the Arrays by the name and completely get rid of the switch you could change your design to storing thisArray, thatArray and anotherArray in a Dictionary, with the keys being the different values for someValue.
This way you could simply append to the desired array with:
arrayDict[someValue]?.append(object)
(Given that you've properly set up the dictionary beforehand)
Like this for example:
enum Value {
case thisArray
case thatArray
case anotherArray
}
var arrayDict = [
Value.thisArray : [String](),
Value.thatArray : [String](),
Value.anotherArray : [String]()
]
arrayDict[.thatArray]?.append("Some String.")
For the sake of creating a short working example I've replaced Object with String but that obviously doesn't matter.
I would typically recommend solving this with closures. It's more powerful and safer. For example:
let append: (Object) -> Void
switch someValue {
case .thisArray: append = { thisArray.append($0) }
case .thatArray: append = { thatArray.append($0) }
case .anotherArray: append = { anotherArray.append($0) }
}
append(object)
(It would be ideal here to just say append = thisArray.append, but you can't do that in Swift today. It's a "partial application of a mutating function" and that's not currently legal.)
Even though Swift was designed to reduce pointer operations, pointers are still available:
var thisArray = [1,2,3]
var thatArray = [4,5,6]
var anotherArray = [7,8,9]
var ptr: UnsafeMutablePointer<[Int]>
let someValue = 2
switch someValue {
case 1: ptr = UnsafeMutablePointer(&thisArray)
case 2: ptr = UnsafeMutablePointer(&thatArray)
default: ptr = UnsafeMutablePointer(&anotherArray)
}
ptr.pointee.append(42)
print(thatArray) // [4,5,6,42]
A minor annoyance with this is that you have to call ptr.pointee to access the target array. If you assign the pointee to another variable (i.e. let whichArray = ptr.pointee), any modification to whichArray won't be reflected in the original array.
(I had to change your Object type to Int so that it runs in the IBM Swift Sandbox)

How to deeply duplicate a multidimensional array in Swift

This question has been asked and answered for a couple other coding languages, but I think I may have a unique problem anyway. So, I want to duplicate a three dimensional array (filled with arbitrary objects). I believe I found that this:
var duplicateArray = originalArray
Does not work, since, for whatever reason, they thought it would a nice safety measure to have this create a duplicate array, but filled with pointers as sub-arrays instead of duplicating the sub-arrays as well. This seems like a strange design choice, since if duplicateArray and originalArray were one-dimensional, this would work as intended. Anyway, so I tried this (where object is some arbitrary object):
var duplicateArray = [[[object]]]()
for x in 0..<originalArray.count {
var tempArrYZ = [[object]]()
for y in 0..<originalArray[x].count {
var tempArrZ = [object]()
for z in 0..<originalArray[x][y].count {
let copiedObj = originalArray[x][y][z]
tempArrZ.append(copiedObj)
}
tempArrYZ.append(tempArrZ)
}
duplicateArray.append(tempArrYZ)
}
This still does not work; all the values in duplicateArray will act like a pointer for their values in originalArray. Perhaps someone has a simple way of deeply duplicating multidimensional arrays, or perhaps someone can find my error?
EDIT: How is this a duplicate of that other question? I'm asking specifically how to "deeply" duplicate. The question that's being referred to nebulously asked about duplicating arrays.
var duplicateArray = originalArray
Would work if the objects are not of reference type. However, for the reference type you need to actually create the copy of the object with copy. Your original code was pretty close.
var duplicateArray = [[[object]]]()
for x in 0..<originalArray.count {
var tempArrYZ = [[object]]()
for y in 0..<originalArray[x].count {
var tempArrZ = [object]()
for z in 0..<originalArray[x][y].count {
let copiedObj = originalArray[x][y][z].copy()
tempArrZ.append(copiedObj)
}
tempArrYZ.append(tempArrZ)
}
duplicateArray.append(tempArrYZ)
}
As already stated, your problem isn't really the copying of the array, it's the copying of Objects. Arrays, like all structs, are copied by value. Objects are copied by reference.
When you copy an array of objects, it's a brand new array with brand new references to the contained objects. Your code is simply creating additional references to the same objects then organizing them in a similar fashion.
Anyway, here's my simpler/functional implementation for copying arrays:
func copyArrayWithObjects <T: Copying>(items: [T]) -> [T]{
return items.map { $0.copy() }
}
func copy2DArrayWithObjects <T: Copying>(items: [[T]]) -> [[T]] {
return items.map(copyObjectsInArray)
}
func copy3DArrayWithObjects<T: Copying>(items: [[[T]]]) -> [[[T]]] {
return items.map(copy2DObjectInArray)
}
Then you can simply do this:
let copiedArray = copy3DArrayWithObjects(originalArray)
Theoretically I think it's possible to create a function to do this for an n-dimension array, but I haven't found a solution yet.
I think it would be best to write an extension on Array that adds conformance to NSCopying, which recursively copies the elements. This solution would be very elegant because it could scale to any number of dimmensions.
Swift arrays are value types so the snippet you provided is fine.
var duplicateArray = originalArray
See this example in a Playground as proof:
var array = [[["test"]]]
var newarray = array
// print different memory addresses
print(unsafeAddressOf(array[0][0][0])) // 0x00007ff7a302a760
print(unsafeAddressOf(newarray[0][0][0])) // 0x00007ff7a33000e0
If you use NSArray or reference types inside the Swift array, then they will no longer copy implicitly and will be treated with the same address - this can also be proved in the Playground. You would need to call copy() explicitly on reference types.

swift function to iterate possibly reversed array

I'd like to create a function that will iterate over an array (or collection or sequence). Then I will call that function with an array, and the reversed version of the array (but efficiently: without creating a new array to hold the reverse).
If I do this:
func doIteration(points: [CGPoint]) {
for p in points {
doSomethingWithPoint(p)
}
// I also need random access to points
doSomethingElseWithPoint(points[points.count-2]) // ignore obvious index error
}
And if I have this:
let points : [CGPoint] = whatever
I can do this just fine:
doIteration(points)
But then if I do this:
doIteration(points.reverse())
I get 'Cannot convert value of type 'ReverseRandomAccessCollection<[CGPoint]> to expected argument type [_]'
Now, I DON'T want to do this:
let reversedPoints : [CGPoint] = points.reverse()
doIteration(reversedPoints)
even though it will work, because that will (correct me if I'm wrong) create a new array, initializing it from the ReverseRandomAccessCollection returned by reverse().
So I guess I'd like to write my doIteration function to take some sort of sequence type, so I can pass in the result of reverse() directly, but ReverseRandomAccessCollection doesn't conform to anything at all. I think I'm missing something - what's the accepted pattern here?
If you change your parameter's type to a generic, you should get the functionality you need:
func doIteration
<C: CollectionType where C.Index: RandomAccessIndexType, C.Generator.Element == CGPoint>
(points: C) {
for p in points {
doSomethingWithPoint(p)
}
doSomethingElseWithPoint(points[points.endIndex - 2])
}
More importantly, this won't cause a copy of the array to be made. If you look at the type generated by the reverse() method:
let points: [CGPoint] = []
let reversed = points.reverse() // ReverseRandomAccessCollection<Array<__C.CGPoint>>
doIteration(reversed)
You'll see that it just creates a struct that references the original array, in reverse. (although it does have value-type semantics) And the original function can accept this new collection, because of the correct generic constraints.
You can do this
let reversedPoints : [CGPoint] = points.reverse()
doIteration(reversedPoints)
or this
doIteration(points.reverse() as [CGPoint])
but I don't think there is any real difference by the point of view of a the footprint.
Scenario 1
let reversedPoints : [CGPoint] = points.reverse()
doIteration(reversedPoints)
Infact in this case a new Array containing references to the CGPoint(s) present in the original array is created. This thanks to the Copy-on-write mechanism that Swift used to manage structures.
So the memory allocated is the following:
points.count * sizeOf(pointer)
Scenario 2
On the other hand you can write something like this
doIteration(points.reverse() as [CGPoint])
But are you really saving memory? Let's see.
A temporary variable is created, that variable is available inside the scope of the function doIteration and requires exactly a pointer for each element contained in points so again we have:
points.count * sizeOf(pointer)
So I think you can safely choose one of the 2 solutions.
Considerations
We should remember that Swift manages structures in a very smart way.
When I write
var word = "Hello"
var anotherWord = word
On the first line Swift create a Struct and fill it with the value "Hello".
On the second line Swift detect that there is no real reason to create a copy of the original String so writes inside the anotherWord a reference to the original value.
Only when word or anotherWord is modified Swift really create a copy of the original value.

Mixing for-in and if-let in Swift

Can I merge a for-in and if-let in one statement?
for item in array {
if let f = item as? NSDictionary {
result.addObject(newFile(f))
}
}
array is made by a JSON, so I don't know if each item is a NSDictionary or not. I have to check.
I was looking for something like this:
for item as? NSDictionary in array {
// code
}
Like Python or Ruby.
#nickfalk is on the right track, but we can do better. His result unfortunately returns [AnyObject], which you can't then call newFile with (I assume). But that's ok, we can get the rest of the way pretty easily.
What you want is partial map. That is to say, you want to map some (but possibly not all) of the elements of one list to another list (from AnyObject to File, if we can). So there must be some rule for choosing, and some rule for mapping. Optional let's us combine those. Let's call the function that does that f. Then its type is:
f: T->U?
So there's some magic function that will possibly convert T to U. We want to map with that. Sounds easy:
extension Array {
func partialMap<U>(f: T->U?) -> [U] {
var result = [U]()
for x in self {
if let u = f(x) {
result.append(u)
}
}
return result
}
}
So now we've hidden all the nasty mutation and var and whatnot down deep where we don't have to look at it. We have a function that takes a mapping function from "something" to "maybe something else" and returns a list of "something elses that we could map."
Now everything is nice and immutable and reusable:
let result = array.partialMap { ($0 as? NSDictionary).map(newFile) }
Whoa there. What's that map in the middle? Well, as? returns NSDictionary?. When you map an optional, then if the optional is None, it returns None, otherwise it applies the function to the value and wraps it in Some. So this whole thing takes AnyObject and returns File? just like we wanted. One partialMap later we have our answer.
I would probably just go for something like:
let result = array.filter() { $0 is NSDictionary }
If you need result to be an NSDictionary array, you can just cast it:
let result = array.filter() { $0 is NSDictionary } as [NSDictionary]
If your goal is to reduce an NSArray to an array only containing NSDictionary filter is a very powerful tool. Create the appropriate filtering function:
func filterForNSDictionary(object: AnyObject) -> Bool{
return object.isKindOfClass(NSDictionary)
}
Then simply pass in you array and function to the filter function
let result = filter(array, filterForNSDictionary)
As #RobNapier points out my solution above will end up with a result array being of the type [AnyObject] this can of course easily be remedied:
let result = filter(array, filterForNSDictionary) as [NSDictionary]
This could be considered risky, if you force the array to be of the wrong type. as [NSString] (for instance9 would most likely blow up in your face down the line...
Rob's solution being pure awesome cleverness of course and #MattGibson delivering the perfect shorthand, while exposing me as an absolute beginner in this field.