Let's say I have list of objects, each containing own array of Strings. I need to find objects that have most duplicates with given array.
I can simply achive that by using some for loops, if and counters, but I want to do that using Java 8 streams. I really hope it is possible.
#Test
public void test() {
String mainArray[] = {"a", "b", "c"};
List<ElementsList> elems = new ArrayList<>();
ElementsList a = new ElementsList(new String[]{"d", "e", "a"});
ElementsList b = new ElementsList(new String[]{"b", "c", "d"});
elems.add(a);
elems.add(b);
List<ElementsList> result = elems.stream()...;
assertTrue(result.contains(b));
}
private class ElementsList {
private String elements[];
private ElementsList(String elements[]) {
this.elements = elements;
}
public String[] getElements() {
return elements;
}
}
I can think of this for example:
List<String> main = Arrays.asList(mainArray);
Stream.of(a, b)
.map(x -> new AbstractMap.SimpleEntry<>(x, new ArrayList<>(new ArrayList<>(Arrays.asList(x.elements)))))
.map(entry -> {
entry.getValue().removeAll(main);
entry.setValue(entry.getValue());
return entry;
})
.sorted(Comparator.comparing(e -> e.getValue().size()))
.map(Entry::getKey)
.forEach(el -> System.out.println(Arrays.toString(el.elements)));
Basically put all the elements into a mutable List and do a removeAllof the ones from mainArray and sort the result based on the size of the remaining ones.
Here's a straightforward approach:
import static java.util.Comparator.comparingLong;
Set<String> mainSet = new HashSet<>(Arrays.asList(mainArray));
ToLongFunction<ElementsList> countMatches = el ->
Arrays.stream(el.getElements())
.filter(mainSet::contains)
.count();
ElementsList result = elems.stream()
.max(comparingLong(countMatches))
.get(); // or throw if elems is empty
This solution is better when lists have different sizes.
List<String> main = Arrays.asList(mainArray);
Stream.of(a, c, b)
.map(x -> new AbstractMap.SimpleEntry<>(x, new ArrayList<>(main)))
.peek(entry -> {
entry.getValue().removeAll(Arrays.asList(entry.getKey().elements));
entry.setValue(entry.getValue());
})
.sorted(Comparator.comparing(e -> e.getValue().size()))
.map(Map.Entry::getKey)
.forEach(el -> System.out.println(Arrays.toString(el.elements)));
Related
I am still not sure about the rules of struct copy or reference.
I want to mutate a struct object while iterating on it from an array:
For instance in this case I would like to change the background color
but the compiler is yelling at me
struct Options {
var backgroundColor = UIColor.blackColor()
}
var arrayOfMyStruct = [MyStruct]
...
for obj in arrayOfMyStruct {
obj.backgroundColor = UIColor.redColor() // ! get an error
}
struct are value types, thus in the for loop you are dealing with a copy.
Just as a test you might try this:
Swift 3:
struct Options {
var backgroundColor = UIColor.black
}
var arrayOfMyStruct = [Options]()
for (index, _) in arrayOfMyStruct.enumerated() {
arrayOfMyStruct[index].backgroundColor = UIColor.red
}
Swift 2:
struct Options {
var backgroundColor = UIColor.blackColor()
}
var arrayOfMyStruct = [Options]()
for (index, _) in enumerate(arrayOfMyStruct) {
arrayOfMyStruct[index].backgroundColor = UIColor.redColor()
}
Here you just enumerate the index, and access directly the value stored in the array.
Hope this helps.
You can use use Array.indices:
for index in arrayOfMyStruct.indices {
arrayOfMyStruct[index].backgroundColor = UIColor.red
}
You are working with struct objects which are copied to local variable when using for in loop. Also array is a struct object, so if you want to mutate all members of the array, you have to create modified copy of original array filled by modified copies of original objects.
arrayOfMyStruct = arrayOfMyStruct.map { obj in
var obj = obj
obj.backgroundColor = .red
return obj
}
It can be simplified by adding this Array extension.
Swift 4
extension Array {
mutating func mutateEach(by transform: (inout Element) throws -> Void) rethrows {
self = try map { el in
var el = el
try transform(&el)
return el
}
}
}
Usage
arrayOfMyStruct.mutateEach { obj in
obj.backgroundColor = .red
}
For Swift 3, use the enumerated() method.
For example:
for (index, _) in arrayOfMyStruct.enumerated() {
arrayOfMyStruct[index].backgroundColor = UIColor.redColor()
}
The tuple also includes a copy of the object, so you could use for (index, object) instead to get to the object directly, but since it's a copy you would not be able to mutate the array in this way, and should use the index to do so. To directly quote the documentation:
If you need the integer index of each item as well as its value, use
the enumerated() method to iterate over the array instead. For each
item in the array, the enumerated() method returns a tuple composed of
an integer and the item.
Another way not to write subscript expression every time.
struct Options {
var backgroundColor = UIColor.black
}
var arrayOfMyStruct = [Options(), Options(), Options()]
for index in arrayOfMyStruct.indices {
var option: Options {
get { arrayOfMyStruct[index] }
set { arrayOfMyStruct[index] = newValue }
}
option.backgroundColor = .red
}
I saw this method in some code and it seems to be working
for (var mutableStruct) in arrayOfMyStruct {
mutableStruct.backgroundColor = UIColor.redColor()
}
I'm starting to learn about closures and want to implement them in a project I'm working on and I'd like some help.
I have a class defined as follows:
class MyObject {
var name: String?
var type: String?
var subObjects: [MyObject]?
}
And I want to use closures or higher oder functions (something like flatMap comes to mind) to flatten an [MyObject] and joining all MyObject and subOjects into one array.
I've tried using [MyObject].flatMap() but this operation doesn't return the nested subObjects.
First, I would highly recommend making the type of subObjects be non-optional. There's rarely a reason for optional arrays. Do you really need to distinguish between "no array" and "an empty array?" This is very uncommon. If you make subObjects just be an array, you can write what you're describing as a simple recursive function:
func flattenMyObjects(myObjects: [MyObject]) -> [MyObject] {
return myObjects.flatMap { (myObject) -> [MyObject] in
var result = [myObject]
result.appendContentsOf(flattenMyObjects(myObject.subObjects))
return result
}
}
If you need it to be optional, the changes are minor (you'll need to add an if-let or something similar).
One approach to flattening a recursive class structure is with a recursive function.
Here is the class that we would like flattened:
public class Nested {
public let n : Int
public let sub : [Nested]?
public init(_ n:Int, _ sub:[Nested]?) {
self.n = n
self.sub = sub
}
}
Here is the function that demonstrates how this could be done:
func test() {
let h = [
Nested(1, [Nested(2, nil), Nested(3, nil)])
, Nested(4, nil)
, Nested(5, [Nested(6, nil), Nested(7, [Nested(8, nil), Nested(9, nil)])])
]
func recursiveFlat(next:Nested) -> [Nested] {
var res = [Nested]()
res.append(next)
if let subArray = next.sub {
res.appendContentsOf(subArray.flatMap({ (item) -> [Nested] in
recursiveFlat(item)
}))
}
return res
}
for item in h.flatMap(recursiveFlat) {
print(item.n)
}
}
The heart of this approach is recursiveFlat local function. It appends the content of the nested object to the result, and then conditionally calls itself for each element to add their contents as well.
In Groovy, how to initialize an array with objects in compact form? I am fed up of making a for-i loop, there must be a Groovyer way?! ;-)
Here's some slightly different scenarios which would be great to cover:
Scenario 1:
Item[] items = new Item[x]
items.initialize { int idx -> new Item(idx) }
Scenario 2:
Item[] items = x.times { int idx -> new Item(idx) }
or..
Item[] items = arrayInit(x) { int idx -> new Item(idx) }
Scenario 3 - here we do not need the index:
Item[] items = x.times { new Item() }
(Must work with #CompileStatic, though - ref the x.times)
This actually work, but it would be great if there was something nicer:
Item[] items = new Items[x]
items.eachWithIndex { entry, int i -> items[i] = new Item(i) }
Taking the following class as Item:
#groovy.transform.ToString
class Item {
def id
}
You can call it in one of the following two ways to produces same output
//You may change it as needed.
def x = 10
def items = (0..<x).collect { new Item(id:it) }
println items
OR
def items = (0..<x).inject([]){li,item -> li << new Item(id:item);li}
println items
OR: If you want only array type and CompileStatic
Item[] items = (0..<x).inject([]){li,item -> li << new Item(id:item);li} as Item[]
println items
Item[] items = new Item[x]
x.times { items[it] = new Item(it) }
Or how about this,
Item[] items = (0..<x).collect{new Item(it)} as Item[]
I have a Protocol called Composite.
This protocol has an array composites: [Composite]
I also have a generic subclass GenericSubclass<T>: Composite
When iterating over the array the best I can come up with looks like this:
for item in composites {
if let item = item as? GenericSubclass<A> {
let sc = SomeOtherClass<A>
} else if let item = item as? GenericSubclass<B> {
let sc = SomeOtherClass<B>
} //and so on...
}
Is there any way to get a hold of GenericSubclass without specifying the Generic? In my use case there is absolutely no need for me to know about the T. I just have to instantiate another class with the same generic type.
Any help is much appreciated.
It's not clear what you're trying to accomplish with the "generic" (pun intended) class names you've chosen. I don't think there's a way to directly accomplish what you want. I.e. you can't just leave it as a generic T because the compiler needs some way to determine what T will be in use at runtime.
However, one way to solve the issue is to hoist the API into the Composite protocol:
protocol Composite {
var composites: [Composite] { get set }
func otherClass() -> OtherProtocol
}
protocol OtherProtocol { }
class GenericSubclass<T>: Composite {
var composites: [Composite] = []
func otherClass() -> OtherProtocol {
return SomeOtherClass<T>()
}
}
class SomeOtherClass<T>: OtherProtocol {}
So now when you implement your loop, you can rely on the fact that since each element is a Composite, you know it must provide an instance of OtherProtocol via the otherClass() method:
var c = GenericSubclass<Int>()
c.composites = [GenericSubclass<Double>(), GenericSubclass<Int>(), GenericSubclass<Character>()]
for item in c.composites {
let sc = item.otherClass()
print(sc)
}
Alternatively, if only GenericSubclass should vend an OtherProtocol, you can make the return type Optional and define an extension for all the other implementations of Composite:
protocol Composite {
var composites: [Composite] { get set }
func optionalClass() -> OtherProtocol?
}
extension Composite {
func optionalClass() -> OtherProtocol? {
return nil
}
}
I did some experiment on this in the playground and i came up with this
protocol Composite {
var composites: [Composite] { get set }
}
class GenericSubclass<T>: Composite {
var composites: [Composite] = []
}
let subclass = GenericSubclass<String>()
for item in subclass.composites {
let className = String(describing: type(of: item))
let aClassType = NSClassFromString(className) as! NSObject.Type
let instance = aClassType.init() // we create a new object
print(instance) //Output: GenericSubclass<String>
}
Hope this will help someone.
I think it's not possible to do that in array.
While you creat some different GenericSubclass<T> then put it in array , you will lose <T> no matter the composites is [Composite] or [Any].
// this line won't compile
let array = [GenericSubclass<Int>(),GenericSubclass<Double>()]
//error: heterogenous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional
You want donging something like this func below, the param should be GenericSubclass<T> to compile success
func genericFunc<T>(param:GenericSubclass<T>) {
let sc = SomeOtherClass<T>()
print(sc)
}
Anyway you can implement it with member var for the instance like the code below:
class Subclass {
var type : Any
init(type : Any) {
self.type = type
}
}
class SomeOtherClass : CustomDebugStringConvertible{
var type : Any
init(type : Any) {
self.type = type
}
var debugDescription: String{
return String(describing: type.self)
}
}
let array : [Subclass] = [Subclass(type : Int.self),Subclass(type : Double.self),Subclass(type : String.self)]
let scArray = array.flatMap {SomeOtherClass(type:$0.type.self)}
print(scArray) // prints [Int, Double, String]
You need to add one method to protocol which creates new item of Type supported this protocol. So now you can use enums, structs and classes without any knowledge of creating object of specific type.
You can play in playground with the following code:
import UIKit
//This is your protocol
protocol MyAwesomeProtocol {
//this methods leaves implementaion detailes
//to concrete type
func createNewObject()->MyAwesomeProtocol
}
//Just create empty string
extension String: MyAwesomeProtocol {
func createNewObject() -> MyAwesomeProtocol {
return String()
}
}
//create Enum with default value
extension UIControlState: MyAwesomeProtocol {
func createNewObject() -> MyAwesomeProtocol {
return UIControlState.normal
}
}
//create viewController of any type
extension UIViewController: MyAwesomeProtocol {
func createNewObject() -> MyAwesomeProtocol {
return type(of:self).init()
}
}
//This is test function
//it creates array of newly created items and prints them out
//in terminal
func doSomeCoolStuffWith(items:[MyAwesomeProtocol]){
var newItems = [MyAwesomeProtocol]()
for anItem in items {
let newOne = anItem.createNewObject()
newItems.append(newOne)
}
print("created new ones:\n\(newItems)\nfrom old ones:\n\(items)\n")
}
doSomeCoolStuffWith(items: [UIControlState.focused,UIControlState.disabled])
doSomeCoolStuffWith(items: [UISplitViewController(),UINavigationController(),UICollectionViewController()])
doSomeCoolStuffWith(items: ["I","love","swift"])
This will produce the following result:
created new ones:
[__C.UIControlState(rawValue: 0), __C.UIControlState(rawValue: 0)]
from old ones:
[__C.UIControlState(rawValue: 8), __C.UIControlState(rawValue: 2)]
created new ones:
[<UISplitViewController: 0x7fa8ee7092d0>, <UINavigationController: 0x7fa8f0044a00>, <UICollectionViewController: 0x7fa8ee705f30>]
from old ones:
[<UISplitViewController: 0x7fa8ee7011e0>, <UINavigationController: 0x7fa8f004e600>, <UICollectionViewController: 0x7fa8ee708fb0>]
created new ones:
["", "", ""]
from old ones:
["I", "love", "swift"]
How do i make 1 array that has the keys of 1st array & its values are the values of the 2nd array in Actionscript 3.0?
Below is my [WRONG] code. Package import left out.
public class myPages extends Sprite {
protected var pageNames:Array = [];
protected var pageLayoutNo:Array = [];
private var pageLayoutNames:Object = new Object();
public function assignNamesLayouts {
//all the names of the pages
for(i=0; i<totalPages; i++) {
var pageMc:MovieClip=new MovieClip();
pageMc.name = menu.config.pages.page[i].#name;
pageNames[i]= pageMc.name;
}
//all layout numbers
for(i=0; i<totalPages; i++) {
pageLayoutNo[i] = menu.config.layoutNum.layNum[i];
}
setNames(pageNames);
setPLNo(pageLayoutNo);
setLayoutNames(pageLayoutNo,pageNames);
}
protected function setNames(a:Array) {
pageNames = a;
}
protected function setPLNo(a:Array) {
pageLayoutNo = a;
}
protected function setLayoutNames(a:Array, b:Array) {
var maps:Object = new Object();
maps.no = a;
maps.nm = b;
for each(var k:int in a) {
maps[k] = b;
}
}
}
Thank you.
I will agree with #Vesper. Closest solution to the associative array will be Dictionary. I also will add, some ideas for you. If your collection will have keys as Strings, you could use simple Object:
var keyName: String = "key1";
var anotherKey: String = "key2";
var collection: Object = {};
collection[keyName] = new MovieClip();
collection[anotherKey] = new Sprite();
trace(collection[keyName]);
If you want to use 2 arrays: one for keys, another one for values, you will need several functions(place object with key, get object by key, get object by value, remove object by key, etc.) to manage them, because indexes in arrays must be identical.
Dictionary is really what you want, but you could do it by creating an array of objects like this:
var newArray=combineArrays("name", arr1,"number",arr2);
function combineArrays(name1, arr1,name2, arr2){
var newArr=new Array();
for(var i<arr1){
var obj=new Object();
obj[name1]=arr1[i];
obj[name2]=arr2[i];
newArr.push(obj);
}
}
Then you can search on the object names or values for either object.