Ha jól értem a problémát, akkor szerintem így lehet egyszerűbben:
A RequestItem-nek definiáld felül a == operátorát, és a hashValue-t. A Set (halmaz) az hash alapján működik, és a kódodban én úgy értelmeztem, hogy elég csak az egyik field-nek azonosnak lennie, hogy a Struct megegyezzen egy másikkal. Szóval ha nálad a content-re kell vizsgálni és az identifier-t cserélni (nem fordítva lenne logikus?) akkor ez szerintem működik:
struct RequestItem: Equatable, Hashable {
var content: String
var identifier: String
var hashValue: Int {
return content.hashValue
}
static func ==(lhs: RequestItem,rhs: RequestItem) -> Bool{
return lhs.content == rhs.content
}
}
Nem kell az index, így hogy meg van írva a hash és a ==, “jól” működik a Set contains metódusa. (alapból ha nem írod felül, ugye akkor a Struct minden mezőjét megnézi, hogy egyezik e)
var requests = Set<RequestItem>()
requests.insert(RequestItem(content: "content", identifier: "indentifier"))
let item = RequestItem(content: "content", identifier: "ok")
requests.update(with: item)
Mivel ugye a content alapján vizsgálja az egyenlőséget, így az update felül fogja írni a régi értéket.
A contains egyébként opcionális, mert ha nincs benne a régi érték a Setben, akkor csak szimplán berakja az újat mint az insert.
Köszi! Ahogy jobban átgondolom, az volt a hibám, hogy az identifier volt az azonosító, amivel a hashable-nél azonosítottam, és azt a contentre kellett volna állítanám.
Egyébként alapvetően abból indult ki a problémám, hogy azt hittem, hogy az azonosítót az Apple adja. De utólag így, hogy én adom, így semmit sem kell felül írnom a Setben. És azért fontos ez, mert a Content néha, nagyon ritkán megváltozhat… mindegy, köszi még egyszer…
Hát az azonosítót alapból az Apple adja, mármint, Struct-okat alapból ha csak nem írod felül, mezőnként hasonlít össze.
Viszont vagy egy másik ötletem a subscript immutable problémára: requests = Set(requests.map({ (item) -> RequestItem in return item.content == "content" ? RequestItem(content: "your new value", identifier: "identifier") : item }))
Content alapján vizsgálod melyik azonos, aztán felülírod a contentet ahol az. Ez működik így, annyi van hogy a műveletigény mindig o(n). Viszont működik ugyanúgy sima tömbre is. Bár a te megoldásod is ennyi, mert egyszer ott is végig kell iterálni az összes elemen.
Tetszik nagyon ez a megoldás, mert “egy sorból áll”. Őszintén, fogalmam sincs, hogy mennyi memóriát vagy processzort igényel egy nagyobb életmű tömbnél.
Bár a find first index első körben biztos gyorsabban találja meg az elemet, de ott meg ott van pluszban a remove at index és az insert.
Amúgy gondolkodtam egy saját extensiönön: replaceAt függvények, de az meg csak egy sorral lenne rövidebb, tehát nem sokat nyernék.
Az Update amúgy ami jó lenne, csak ott meg az volt a baj, hogy pont sz egyedi azonosítót nem tudtam.
Egyébként nyilván nem volt teljes a kód, amit leírtam, mert az komplexebb lett volna. Local notification kiküldése, majd az alkalmazás újra indítása után (vagy előtérbe kerülésekor) a visszaellenőrzés volt a cél, hogy tudjam mi van függőben. És nem tudtam, hogy a notification kiküldésekor nekem kell megadnom az identifiert. Így már nincs szükség a fentebbi kódra. :)
Hát ugye az mondjuk egy normál tömbre O(n). Mert a first index gondolom addig iterál a tömbben amíg meg nem találja a keresett dolgot, nem tovább. Ez ugye lehet 1 de 100 is legrosszabb esetben egy n=100 elemű tömbnél. Maga a törlés az O(1), viszont utána a maradék elemet shiftelni kell eggyel balra. Ha az utolsót töröltük akkor nincs mit shiftelni de előtte ugye végig kell menni a tömbön. Ez eddig n. Viszont ha be is akarsz szúrni egy új elemet az még ugye megint járhat shifteléssel, ha ugyan oda szeretném berakni és nem csak a tömb végére. szóval legrosszab esetben ez + n tehát összesen 2n ami O(n).
Setnél kicsit más a helyzet, mert az elemek nem egymás mellett vannak a memóriában így ott a shiftelés nem szükséges. Viszont épp ezért az indexeléssel is vigyázni kell, mert az index csak akkor biztosan valid amikor lekérdezed, később lehet, hogy ott nincs adat, és nem definiált viselkedéshez vezet. Valahol a doksiban volt ez bővebben leírva, de most nem találom.
Utólag belegondolva az én megoldásom is 2n, mert van egy Set konstruktor hívás is ami gondolom úgy működik, hogy n szer meghívja az insert-et.
Belefutottam egy érdekes problémába. Szerintem a Swift fordító ott ad hibát, ahol nagyon nem kéne, főleg nem az alábbi hibaüzenettel: “Use of local variable ‘images’, before its declaration.”
Itt az adott kódrészlet:
Elég egyértelmű, hogy a 34.sorban létrehozott változó elérhető a viewDidLoad() metóduson belül. Viszont abban a pillanatban, ahogy a 41. sorban létrehozok egy újabb images nevű változót, (ugye ez teljesen valid, szimplán csak elfedi az előzőt) a fordító feldobja ezt a hibát, aminek egyszerűen semmi értelme nincsen. (Nyilván ha a 38.sorban self.images-el hivatkoznék a változóra, akkor működne, de ennek így is kéne.) Olyan mintha a fordító először a függvényen belüli változókat nézné, és nem látná a külsőt, hiszen akkor teljesen igaza lenne, de így ez értelmezhetetlen.
Minden normális nyelvben (pl JAVA-ban) ez teljesen valid:
class A {
public static void main(String[] args) {
new B();
}
}
class B {
int a = 20;
B() {
a = 2;
int a = 10;
System.out.println(a); // a = 10
}
}
Kérdés: vajon ez szándékosan működik ilyen hülyén (most attól tekintsünk el, hogy ritkán csinál ilyet az ember, én is teljesen véletlenül futottam bele.), vagy hiba van a fordítóban?
En azt mondanam, hogy igaza van. A valtozo deklaracio blokk-szintu, tehat a 41. soros let a viewDidLoad scope-jaba tartozik, ugyhogy azon a szinten valoban duplikalt a valtozo.
(Modern) javascriptben ugyanigy van, hagyomanyosban meg rosszabb :)
let a = 5;
(function(){
console.log(`a before: ${a}`); // undefined
let a = 7;
console.log(`a after: ${a}`); // 7
})();
De a blokk szintűség csak annyit jelent, hogy létrejön a 41.sorban és a metódus végén megszűnik. Attól még a 41.sor előtt a metódusban a másikkal azt csinálok amit szeretnék. A 41. sor után annyi történik, hogy onnantól az images az a lokális images-t jelenti. A self.images viszont jó. Ez így szerintem ebben a formában furcsa.
Igen, rosszul fogalmaztam, a blokk az valóban { -tól } ig tart, de a változó hatóköre az a létrehozásától, a blokk végéig tart, nem pedig a blokk elejétől. De úgy látszik, akkor ez nem egy egyértelmű konvenció szerint van, ahány nyelv annyi implementáció.
Őszintén szólva szerintem is jól van így. Ugyanis felhívja a figyelmet egy lehetséges hibára. Ráadásul később (akár már 1-2 hónappal) olvashatatlan lesz így a kód, azaz nem tudjuk mi honnan van. Az egy dolog, hogy működnie kellene és ebből a szempontból igazad van, mégis azt mondom, hogy jó ez így.
Ráadásul! Ha egy másik fejlesztőnek eszébe jut, hogy “á, még A sor előtt le kellene futtatni valamit” és beteszi a kódját a “global” images elé, máris borult, hogy ki kivel van.
Ezek miatt sem értem, hogy miért hagyják el a self “előtagot” a programozók? Fáj kiírni vagy mert több helyet foglal?
Amúgy én megszüntetném a self nélküli hívásokat.
Elfogadom, hogy így gondoljátok, ebben is van logika. Egyébként én pl soha nem írom ki a self-et, csak ha muszáj (pl closure-ben) :D. Szerintem zavaró, és csak a helyet foglalja, feleslegesen nem írom le “kétszer” ugyanazt. Nem életszerű olyan kódot írni, amit self nélkül nehéz lenne értelmezni. (eltekintve pl a konstruktoron belüli értékadásnál stb)
Lényegében ugyanaz a helyzet a Swiftben is szerintem.
Ebben viszont egyetertek. A sok “self”, “this” inkabb csak zavaro. Celszerubb elkerulni az ugyanolyan nevu lokalis valtozokat. Szamomra az egyetlen elfogadthato kivetel, ahogy irtad is a konstruktor parameterei.
En is azt tamogatnam, hogy ki kelljen irni a self/thist mindenhova. Szamomra sokkal olvashatobb, ha mar ott akkor latom, hogy amit ott piszkalok az honnan jon. De elkepzelheto, hogy ez tenyleg megszokas kerdese, mert en ugy nottem fel, hogy ezek nem voltak opcionalisak.
+1 javaban lehet statikus metódust hívni simán a nevével, vagy osztálynév+metódus formában. Itt zavaró lehet, hogy nem lehet “ránézésre eldönteni” melyik metódus statikus , melyik nem.
De a Swiftben ugye ez is megoldott, mert kötelező kiírni az osztály/struct nevét előtte, tehát a self nem ad hozzá semmit a megérthetőséghez.
Szerintem ha valaki betartja a konvenciókat, nem ír 100soros függvényeket, szépen tagolja a kódot, akkor nem fog olyan helyzetbe kerülni, hogy nem egyértelmű amit leírt. Én a Swiftlint-et használom, nálam bevált.
guard let self = self else { return } Ez meg külön vicces, hogy csak keyword, és nem csak, hogy valid kód, de hányszor futottam már bele, nem csak saját kódban. Amikor ezt kitalálták, hogy így jó lesz, gondolom akkor sem az érthetőség volt a fő szempont. :D
Ha egy ember ír egy kódot, akkor még talán elfogadom azt, hogy esetleg ne írjuk ki mindenhová. Bár ahogy írtam én is kiírom mindig, mert egyértelmű. Bár amúgy az xcode színezése is egyértelművé teszi. De!
Ahogy szóba került akár 100 soros blokk végén valaki használja a példány változóját self nélkül. Majd hónapokkal később eszébe jut, hogy a blokk elején ellenőrizni kellene valamit és ugyanezt a változónevet használja lokálisan. Onnantól kezdve fel is borult az egész és csak pislog, hogy milyen új hibák keletkeztek. (persze ehhez az kell, hogy a lokális változó típusa megegyezzen.)
Pl:
var images = [UIImage]()
func valami() {
.... // akár 100 sorközben...
let akarmi = images.map{...}
}
majd később hónapokkal.
func valami() {
let images = masikImages.filter{...}
.... // akár 100 sorközben...
let akarmi = images.map{...}
}
És kész a probléma. És értem én, hogy oda kell figyelni és nem lesz gond, de így könnyne előjöhet a hiba. Nem beszélve arról, hogy ha többen is írják a kódot egyszerre, akkor lehet az egyik odafigyel ilyenekre, de ha a másik fél nem, akkor meg is történt a baj.
De amúgy meg akinek fáj kiírni azne írja ki, aki meg nem bír meglenni nélküle az írja ki. Nekem pont az fáj, ha nincs kiírva :D.
Az eredeti kérdésre reagálva még: én örülnék neki ha ilyen esetben figyelmeztetne engem az Xcode.
Nem hasznalunk olyan lokalis valtozot, ami elfed egy globalisat. (barmit ami feljebb mar definialva van.) Ez alol max az egy betus ciklusvaltozok lehetnek kivetelek, bar celszeruen azok sem.
Van annyi lehetoseg elnevezni dolgokat… Raadasul ha nem ugyanaz, akkor miert hivnam ugyanugy?
Arról nem is beszélve, hogy senki nem ír 100soros függvényt, de még 20 sorost sem. Ha ennél több sor valami akkor nagy valószínűséggel ott ki lehetne a dolgokat szervezni másik függvénybe, osztályba.
Egy tíz soros függvényben meg azért észreveszi az ember simán ha ilyet eset felmerül,hogy elfedi a lokális változó az osztály fieldjét.
Az évek során már láttam jónéhány kódot, amit másoktól vettem át. Vagy templateként árulják x összegért… megrendelő úgy gondolta, hogy így sokkal gyorsabb lesz a fejlesztés… hát nem.
Ha láttátok volna azokat a kódokat akkor értenétek a “félelmem”.
És ez a “változó probléma” csak a jéghegy csúcsa. Legutóbbi kódom tele volt olyan optional változókkal, amiket ! jellel unwrappoltak szinte mindenhol. Mivel az android programozó kényszerűségből szenvedett a kóddal. Aztán az adatbázis kapcsolat mind-mind a main loopon megy megfagyasztva ezzel az egész ui-t szinkronizálás alatt.
És még hosszan sorolhatnám. Tehát elhiszem, hogy aki tud programozni az betartja a szabályokat és minden tökéletes. De hát emberek írják a kódokat nem pedig agyonszabályzott gépek.
Najó, de ha valaki egyáltalán nem ért hozzá, az ne nyúljon a kódhoz.
“Tehát elhiszem, hogy aki tud programozni az betartja a szabályokat és minden tökéletes. De hát emberek írják a kódokat nem pedig agyonszabályzott gépek.”
Jó esetben mindenkinek be kell tartania a szabályokat, konvenciókat, különben az eredmény olyan is lesz. Az nem mentség a gányolásra, hogy nem ért hozzá. Mondjuk a projektvezető hibája is, hogy olyan feladatot ad ki amihez a kolléga egyáltalán nem ért. De miután összegányolta, utána valakinek ki kell javítania az egészet, és dupla annyi pénz és idő megy el a dologra.