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[]
Related
I've a NSObject class as follows:
class Number: NSObject {
var number: Int = 0
init(newNumber: Int) {
self.number = newNumber
}
}
And then I'm creating an array of this Number object as follows:
var numbersArr = [Number(newNumber: 1), Number(newNumber: 2)]
Now I want to change number property of an element of the existing array and append it as a new element. I'm doing it like:
var newItem = numbersArr[1]
newItem.number = 4
numbersArr(newItem)
I expect new value of numbersArr to be [1, 2, 4] but what I get is [1, 4, 4].
I understand that I'm changing the value of existing element instead of creating a new one and then appending it. But how can I achieve the desired result?
I can't initialize a new Number object and then append it. There are some other properties also in my actual NSObject class which needs to remain same.
That's not possible. The nature of reference types is that changing a value for one reference changes it for all of them. Your only options are:
Use a struct instead
Initialize a new object
Assuming using a struct isn't an option, you might try adding a method to your class to copy all its values to a new object:
class Number: NSObject {
var number: Int = 0
init(newNumber: Int) {
self.number = newNumber
}
func copy () -> Number {
return Number(newNumber: self.number)
}
}
And then:
let newItem = numbersArr[1].copy()
newItem.number = 4
numbersArr.append(newItem) // [1, 2, 4]
You problem is a common one when dealing with reference type. You are index 1 and 2 of numbersArr point to the same object so when you modify one, you change the other too.
You need to make a deep copy:
class Number: NSObject, NSCopying {
var number: Int = 0
init(newNumber: Int) {
self.number = newNumber
}
func copy(with zone: NSZone? = nil) -> Any {
return Number(newNumber: self.number)
}
}
var numbersArr = [Number(newNumber: 1), Number(newNumber: 2)]
let newItem = numbersArr[1].copy() as! Number
newItem.number = 4
numbersArr.append(newItem)
Almost same as like Jhon Montgomery's solution, but small update on it.
func copy () -> Number {
let number = Number(newNumber: self.number)
//Copy your all other NSObject properties here
//Example
number.example = self.example
return number
}
Now Try,
let newItem = numbersArr[1].copy() // Here it copies your number with all NSObject properties.
newItem.number = 4
numbersArr.append(newItem) // [1, 2, 4]
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)));
In Swift playground, I'm using a for loop to insert items. During the loop it seems to be adding the right values. At the end of the for loop, when the array is all filled up, when I query, it shows only the value which was inserted last at each index.
New to swift, so I guess am doing something really stupid here... Need help...
The code is as below:
class card {
var suit: String = ""
var rank: String = ""
}
var card1 = card()
var deck = [card]()
var playingCard = card()
var suits = ["♠︎","♣︎","♥︎","♦︎"]
var ranks = ["A","K","Q","J","10","9","8","7","6","5","4","3","2"]
let countRanks = ranks.count
let countSuits = suits.count
var ctr = 0
for i in 0..<countSuits {
for j in 0..<countRanks {
playingCard.rank = ranks[j]
playingCard.suit = suits[i]
deck.insert(playingCard, atIndex: 0)
println("\(deck[ctr].suit)"+"\(deck[ctr].rank)")
ctr++
}
}
let x = deck.count
for i in 0..<x {
println("\(deck[i].rank)"+"\(deck[i].suit)")
}
class instances are reference types. You need to create a new card instance for each iteration. Move the line var playingCard = card() right after the for j in 0..<countRanks line and change var to let
You need to initialise a new instance of playingCard each iteration:
for i in 0..<countSuits {
for j in 0..<countRanks {
var playingCard = card()
...
}
}
I would also suggest you add an init method for your card class to make it easier to initialise:
class Card {
var suit: String
var rank: String
init(suit: String, rank: String) {
self.suit = suit
self.rank = rank
}
}
...
var playingCard = Card(suit: suits[i], rank: ranks[j])
I want to store structs inside an array, access and change the values of the struct in a for loop.
struct testing {
var value:Int
}
var test1 = testing(value: 6 )
test1.value = 2
// this works with no issue
var test2 = testing(value: 12 )
var testings = [ test1, test2 ]
for test in testings{
test.value = 3
// here I get the error:"Can not assign to 'value' in 'test'"
}
If I change the struct to class it works. Can anyone tell me how I can change the value of the struct.
Besides what said by #MikeS, remember that structs are value types. So in the for loop:
for test in testings {
a copy of an array element is assigned to the test variable. Any change you make on it is restricted to the test variable, without doing any actual change to the array elements. It works for classes because they are reference types, hence the reference and not the value is copied to the test variable.
The proper way to do that is by using a for by index:
for index in 0..<testings.count {
testings[index].value = 15
}
in this case you are accessing (and modifying) the actual struct element and not a copy of it.
Well I am going to update my answer for swift 3 compatibility.
When you are programming many you need to change some values of objects that are inside a collection. In this example we have an array of struct and given a condition we need to change the value of a specific object. This is a very common thing in any development day.
Instead of using an index to determine which object has to be modified I prefer to use an if condition, which IMHO is more common.
import Foundation
struct MyStruct: CustomDebugStringConvertible {
var myValue:Int
var debugDescription: String {
return "struct is \(myValue)"
}
}
let struct1 = MyStruct(myValue: 1)
let struct2 = MyStruct(myValue: 2)
let structArray = [struct1, struct2]
let newStructArray = structArray.map({ (myStruct) -> MyStruct in
// You can check anything like:
if myStruct.myValue == 1 {
var modified = myStruct
modified.myValue = 400
return modified
} else {
return myStruct
}
})
debugPrint(newStructArray)
Notice all the lets, this way of development is safer.
The classes are reference types, it's not needed to make a copy in order to change a value, like it happens with structs. Using the same example with classes:
class MyClass: CustomDebugStringConvertible {
var myValue:Int
init(myValue: Int){
self.myValue = myValue
}
var debugDescription: String {
return "class is \(myValue)"
}
}
let class1 = MyClass(myValue: 1)
let class2 = MyClass(myValue: 2)
let classArray = [class1, class2]
let newClassArray = classArray.map({ (myClass) -> MyClass in
// You can check anything like:
if myClass.myValue == 1 {
myClass.myValue = 400
}
return myClass
})
debugPrint(newClassArray)
To simplify working with value types in arrays you could use following extension (Swift 3):
extension Array {
mutating func modifyForEach(_ body: (_ index: Index, _ element: inout Element) -> ()) {
for index in indices {
modifyElement(atIndex: index) { body(index, &$0) }
}
}
mutating func modifyElement(atIndex index: Index, _ modifyElement: (_ element: inout Element) -> ()) {
var element = self[index]
modifyElement(&element)
self[index] = element
}
}
Example usage:
testings.modifyElement(atIndex: 0) { $0.value = 99 }
testings.modifyForEach { $1.value *= 2 }
testings.modifyForEach { $1.value = $0 }
How to change Array of Structs
for every element:
itemsArray.indices.forEach { itemsArray[$0].someValue = newValue }
for specific element:
itemsArray.indices.filter { itemsArray[$0].propertyToCompare == true }
.forEach { itemsArray[$0].someValue = newValue }
You have enough of good answers. I'll just tackle the question from a more generic angle.
As another example to better understand value types and what it means they get copied:
struct Item {
var value:Int
}
func change (item: Item, with value: Int){
item.value = value // cannot assign to property: 'item' is a 'let' constant
}
That is because item is copied, when it comes in, it is immutable — as a convenience.
Had you made Item a class type then you were able to change its value.
var item2 = item1 // mutable COPY created
item2.value = 10
print(item2.value) // 10
print(item1.value) // 5
This is very tricky answer. I think, You should not do like this:
struct testing {
var value:Int
}
var test1 = testing(value: 6)
var test2 = testing(value: 12)
var ary = [UnsafeMutablePointer<testing>].convertFromArrayLiteral(&test1, &test2)
for p in ary {
p.memory.value = 3
}
if test1.value == test2.value {
println("value: \(test1.value)")
}
For Xcode 6.1, array initialization will be
var ary = [UnsafeMutablePointer<testing>](arrayLiteral: &test1, &test2)
It is possible to use the map function to get this effect - essentially creating a new array
itemsArray = itemsArray.map {
var card = $0
card.isDefault = aCard.token == token
return card
}
I ended up recreating a new array of struct see the example below.
func updateDefaultCreditCard(token: String) {
var updatedArray: [CreditCard] = []
for aCard in self.creditcards {
var card = aCard
card.isDefault = aCard.token == token
updatedArray.append(card)
}
self.creditcards = updatedArray
}
I tried Antonio's answer which seemed quite logical but to my surprise it does not work. Exploring this further I tried the following:
struct testing {
var value:Int
}
var test1 = testing(value: 6 )
var test2 = testing(value: 12 )
var testings = [ test1, test2 ]
var test1b = testings[0]
test1b.value = 13
// I would assume this is same as test1, but it is not test1.value is still 6
// even trying
testings[0].value = 23
// still the value of test1 did not change.
// so I think the only way is to change the whole of test1
test1 = test1b
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.