webForumDet fria alternativet

Array.indexOf()

9 svar · 864 visningar · startad av stattin

stattinMedlem sedan jan. 2005713 inlägg
#1

Hej wFare!

Satt och knåpade ihop den här och tänkte få höra lite ros eller ris:

function getIndexPosOf(value, arr) {

	var sep = "#######"
	//Sätt en passande separator. Får inte förekomma i arrayen!
	
	var multipleSuccessReturnValue = true; //true; or false;
	//true = Returnerar ett eller flera värden om värdet i arrayen förekommer mer än en gång.
	//false = Returnerar ett värde ovasett.

	var failReturnValue = false;
	//Vad som ska returneras vid misslyckad sökning:
	
	
		var result = [];
			var arr = arr.join(sep)		
			var arr_str = sep + arr.toString() + sep;
		var value = sep + value + sep;
		var valueLength = arr_str.split(value).length;

		var removed = sep + "*removed*" + sep
		
		if(arr_str.match(value)){
				for(i=0;i<valueLength-1;i++){
					result.push(arr_str.split(value)[0].split(sep).length-1)
					arr_str = arr_str.replace(value, removed)
						if(!multipleSuccessReturnValue){
							return result;
					}
				}
			return result;
		}
	return failReturnValue;
}

Jag har sett andra script som gör samma sak men oftast genom att lopa igenom hela arrayen vilket jag ville undvika men frågan är ju om det går snabbare för det.

Hur hade ni gjort för att lösa samma sak? Hur hade ni snyggat till den här koden?

Tanken var att efterlikna denna (som bara funkar i FX då):

searchString = 'red';
colours = ['green', 'red', 'blue', 'red', 'orange'];
var found = colours.indexOf(searchString);

http://www.stellapower.net/content/javascript-support-and-arrayindexof-ie

MVH Stattin och godnatt!

/EDIT
Fixade scriptet lite

dAEkMedlem sedan feb. 20041 816 inlägg
#2

Jag kan bistå med det ena och det andra. ;)

  • Borde det inte vara snabbare att använda en map (associative array), dvs arrayens nycklar?
  • Om man deklarerar sina variabler blir koden mycket lättare att förstå (tänk underhåll).
  • Var inte rädd för långa variabel- eller funktionsnamn! Det går att optimera i efterhand t.ex. m.h.a. YUICompressor, minify eller liknande. Ska man använda någon annans kod vill man gärna att den ska vara lätt att förstå. gio() säger inte mig ett smack. GetIndexPositions(value, itemArray) tycker jag säger mer om vad man kan förvänta sig av funktionen bara för att bidra med ett exempel på bättre namnkonvention lite snabbt.
  • Rubriken säger Array.indexOf() men din implementation bryter mot hur indexOf() normalt fungerar. Är det meningen?
  • Returnera en (int)array istället för en kommaseparerad sträng.
  • Jag hade wrappat funktionen inom ett eget "namespace"/klasstruktur eller vad man kallar det i Javascriptvärlden.

Hoppas att de här punkterna kan vara till någon hjälp.

stattinMedlem sedan jan. 2005713 inlägg
#3

dAEk skrev:

Jag kan bistå med det ena och det andra. ;)

  • Borde det inte vara snabbare att använda en map (associative array), dvs arrayens nycklar?

jo men den array jag ska ha scriptet till har inga nycklar, och det blir väl inte riktigt samma sak ändå, eller?

dAEk skrev:

Om man deklarerar sina variabler blir koden mycket lättare att förstå (tänk underhåll).

var result = [];
var str = "," + arr.toString() + ",";
var value = "," + value + ",";

dAEk skrev:

Var inte rädd för långa variabel- eller funktionsnamn! Det går att optimera i efterhand t.ex. m.h.a. YUICompressor, minify eller liknande. Ska man använda någon annans kod vill man gärna att den ska vara lätt att förstå. gio() säger inte mig ett smack. GetIndexPositions(value, itemArray) tycker jag säger mer om vad man kan förvänta sig av funktionen bara för att bidra med ett exempel på bättre namnkonvention lite snabbt.

Ja jag var nog inte ute efter att komprimera ner det så lite som möjligt, tyckte det vart enklare bara att skriva gio() än GetIndexPositions() varje gång man skulle kalla på funktionen.

dAEk skrev:

Rubriken säger Array.indexOf() men din implementation bryter mot hur indexOf() normalt fungerar. Är det meningen?

Hade detta i åtanke när jag skrev rubriken:

searchString = 'red';
colours = ['green', 'red', 'blue', 'red', 'orange'];
var found = colours.indexOf(searchString);

dAEk skrev:

Returnera en (int)array istället för en kommaseparerad sträng.

Menar du så:

result = [];
...
push.result(str.split(value)[0].split(',').length-1)
...
return result;

dAEk skrev:

Jag hade wrappat funktionen inom ett eget "namespace"/klasstruktur eller vad man kallar det i Javascriptvärlden.

Jag har inte hängt med i utvecklingen tror jag :r. Någon som kan visa ett enkelt exempel eller länka till ett.

nitro2k01Medlem sedan aug. 20039 342 inlägg
#4

När dAEk säger att din implementation bryter mot standard så syftar han nog på att din implementation returnerar false istället för -1 när sökningen misslyckas. Dessutom, standard-indexOf returnerar bara ett värde. Varför ska din funktion returnera mer än så, förutom för att du kan?

Har du för övrigt funderat på vad som händer om något värde, som ligger före eventuell matchning, innehåller ett kommatecken?
Eftersom du jobbar med en sträng och inte en array så har du inget sätt att urskilja kommatecknens betydelse. Du kan mildra effekten genom att använda arr.split('något') istället för arr.toString() men du kommer inte runt själva problemet på det viset: Skulle någon råka ha med dina magiska separator i något värde så kommer din funktion inte funka korrekt.

stattin skrev:

jo men den array jag ska ha scriptet till har inga nycklar, och det blir väl inte riktigt samma sak ändå, eller?

Jag gissar att han menar att du ska skapa en "baklänges-array" där du lägger till varje element med värdet som nyckel, och nyckeln som värde. Kan återkomma med lite kod-exempel.

För övrigt, sluta förkorta Firefox till FX. Det officiella uttalande som brukar åberopas är FAQ'n i release notes för version 1.5. Den versionen kom ut 29 november 2005 och slute stödjas officiellt 30 maj 2007. Mig veterligen finns inget nyare officiellt uttalande om hur man bör förkorta Firefox, och FF är de facto den absolut populäraste förkortningen.

stattinMedlem sedan jan. 2005713 inlägg
#5

nitro2k01 skrev:

När dAEk säger att din implementation bryter mot standard så syftar han nog på att din implementation returnerar false istället för -1 när sökningen misslyckas. Dessutom, standard-indexOf returnerar bara ett värde. Varför ska din funktion returnera mer än så, förutom för att du kan?

Kort sagt så ska den inte verka som indexOf. Den ska retunera mer eftersom jag vill veta värdets alla positioner.

nitro2k01 skrev:

Har du för övrigt funderat på vad som händer om något värde, som ligger före eventuell matchning, innehåller ett kommatecken?
Eftersom du jobbar med en sträng och inte en array så har du inget sätt att urskilja kommatecknens betydelse. Du kan mildra effekten genom att använda arr.split('något') istället för arr.toString() men du kommer inte runt själva problemet på det viset: Skulle någon råka ha med dina magiska separator i något värde så kommer din funktion inte funka korrekt.

Jodå jag har tänkt på det men jag hade inte tänkt massproducera scriptet heller så denna någon kan nog inte bli någon annan än jag. Men om nån vill ha det så ta det.

nitro2k01 skrev:

Jag gissar att han menar att du ska skapa en "baklänges-array" där du lägger till varje element med värdet som nyckel, och nyckeln som värde. Kan återkomma med lite kod-exempel.

Tacksamt!

nitro2k01 skrev:

För övrigt, sluta förkorta Firefox till FX. Det officiella uttalande som brukar åberopas är FAQ'n i release notes för version 1.5. Den versionen kom ut 29 november 2005 och slute stödjas officiellt 30 maj 2007. Mig veterligen finns inget nyare officiellt uttalande om hur man bör förkorta Firefox, och FF är de facto den absolut populäraste förkortningen.

Ska säga FX så länge jag lever :birp

dAEkMedlem sedan feb. 20041 816 inlägg
#6

stattin skrev:

jo men den array jag ska ha scriptet till har inga nycklar, och det blir väl inte riktigt samma sak ändå, eller?

Näe, det stämmer. Vet inte ens om jag tycker att det är ok att (fel)använda arrayobjektet på det sättet. Det var dumt att ta upp det ö.h.t. Sorry! Strunta i den biten.

stattin skrev:

Ja jag var nog inte ute efter att komprimera ner det så lite som möjligt, tyckte det vart enklare bara att skriva gio() än GetIndexPositions() varje gång man skulle kalla på funktionen.

Ok, jag förstår tänket men håller inte med. Kodar man på det sättet slutar det med namnkonflikter och obegripliga funktioner. Fast det var kanske inte tänkt att vi skulle diskutera olika kodstilar...

Som nitro2k01 skriver syftade jag på att din implementation bryter mot standard då den

  1. returnerar en array. indexOf() returnerar normalt bara den första positionen. Hittar den inget som matchar parametern får man -1 tillbaka.
  2. returnerar en sträng. Om det är positionerna man frågar efter är det dem man borde få tillbaka. Positionerna i en array är aldrig en sträng utan heltal.

I efterhand är det tydligare att det inte var meningen att implementera en kopia av indexOf() utan istället en utökad variant som påminner om funktionen. Exemplet du hade med vilseledde mig eftersom det beskrev hur indexOf fungerar och inte hur du vill att ditt skript ska funka. :)

I alla fall...

Det uppdaterade skriptet tycker jag går i helt fel riktning. Jag fattar inte varför det använder sig av en separator ö.h.t. eller varför du håller på och konverterar arrayen till en sträng och sedan kör replace, split osv.
Vi ska inte hålla på och böka med olika datatyper eftersom det bara blir merjobb, ökar risken för feltänk och buggar etc. Vi har en generell datahållare (arrayen) att söka i, that's it.

Vi vill att funktionen ska svara med de positioner som stämmer med sökkriteriat, då tycker jag att den alltid ska returnera en array. Hittas inga element, ja då returnerar man en tom array. Inte false, inte null utan en tom array. Varför? Jo, för det första vet man alltid vad man får tillbaka från funktionen så det blir mycket lättare att använda den. För det andra behöver man inte en massa olika specialfall utan man kan lätt använda arrayen och dess properties (t.ex. length) för att hantera resultatet. Koden blir mycket lättare att förstå om man håller sig till en datatyp.

Nog om det, låt oss gå vidare till lite kodning.

Att IE saknar stöd för Array.indexOf(), det vet vi. Vi vet också att andra webbläsare har stöd för den funktionen och då tycker jag att vi låter dessa webbläsare fortsätta använda sin implementation eftersom den kommer vara snabbare än vår men utöver det är vi nog mer benägna att bygga in buggar. :) Vi lägger funktionaliteten i en sk. wrapper, i denna känner vi av vilken metod vi ska använda: webbläsarens egna eller vår.

Som jag skrev tidigare vill vi kapsla in våra metoder i ett eget scope. JavaScript har inte namespaces ännu (det är inte implementerat än) men man kan fixa samma grej genom att skapa sig en egen klasstruktur. Det finns olika sätt att göra det på men jag har nyligen blivit förtjust i att använda object literal notation som det så fint heter:

var Stattin = {
	ArrayUtil : { }
};

Här skapar vi oss en static utilityklass (eller snarare object?) "Stattin" och i det reserverar vi "ArrayUtil", som vi strax skall komplettera med funktionalitet. Först deklarerar vi alltså strukturen, sedan fyller vi på med logik.

var Stattin = {
	ArrayUtil : { }
};
Stattin.ArrayUtil.getIndexPositions = function(value, array) {
	//här lägger vi vår logik
};

Tidigare skrev jag att man vill låta webbläsare som har en implementering av Array.indexOf() fortsätta använda den så det behöver vi känna av innan vi kodar vidare.

Stattin.ArrayUtil.getIndexPositions = function(value, array) {
	if (array.indexOf != undefined) {
		return nativeIndexOf(value, array);
	} else {
		return customIndexOf(value, array);
	}
	
	//interna funktioner
	function nativeIndexOf(value, array) {
		//stöd för Array.indexOf finns redan. Använd det.
	}
	
	function customIndexOf(value, array) {
		//vår egen logik läggs här
	}
};

Nu har vi kommit en bra bit på vägen. Det som fattas är att lägga in själva logiken för hur vi hittar alla positioner. Jag fuskade lite och tog hjälp av Google för att hitta kort och koncis kod för webbläsare som har stöd för Array.indexOf.
Vår egna algoritm gjorde jag så enkel som det bara går. Behöver man optimera gör man det senare, först behöver man en fungerande implementation och lättläslig kod är lättare att finputsa. Eftersom den är så enkel gissar jag att den även är snabbare än att hålla på och joxxa med regex, split, kopiera arrayer osv men det är inte det viktiga i sammanhanget anser jag. Det är fritt fram att såga mina slutsatser om man vill. :)

Stattin.ArrayUtil.getIndexPositions = function(value, array) {
	if (array.indexOf != undefined) {
		return nativeIndexOf(value, array);
	} else {
		return customIndexOf(value, array);
	}
	
	function nativeIndexOf(value, array) {
		var positions = [];
		var position = array.indexOf(value);
		
		while (position != -1) {
			positions.push(position);
			position = array.indexOf(value, position + 1);
		}
		
		return positions;
	}
	
	function customIndexOf(value, array) {
		var positions = [];
		
		for (var i = 0; i < array.length; i++) {
			if (value === array[i]) {
				positions.push(i);
			}
		}
		
		return positions;
	}
};

Sen så använder man det genom att anropa det:

Stattin.ArrayUtil.getIndexPositions("green", testArray2);

Det här sättet är inte nödvändigtvis det bästa sättet att göra det på men det är en bra grund. Att bygga in nya funktioner är lika enkelt och i.o.m. att vi kapslar in funktionaliteten minskar riskerna för t.ex. namnkollisioner. Namnger man sina filer och klasser på ett smart sätt får man en tydlig kodbas som är lätt att underhålla.

Hoppas att du får ut nåt av allt mitt svammel. :bire

stattinMedlem sedan jan. 2005713 inlägg
#7

Jag är med på att jag bökar till det med arr.toString() osv, men tanken är att jag vill försöka fixa det utan att loopa igenom hela arrayen. Manuellt då.

Tänk det så här, en array på kanske 8000 värden.
En annan array på kanske 700 värden.
Varje värde i den med 700 ska gämföras med den på 8000.

Ditt fall: 700 anrop och varje anrop måste loopa 8000 gånger.

Mitt fall: 700 anrop och varje anrop måste loopa allt från 0 och kanske upp till 8000 men det kan ju även bli 45 eller 3 eller 7.

Nu har jag iofs inte gämfört dessa fall så jag ska inte uttala mig om vilket som kan vara snabbast.

Tack för att du tar dig tid, nu ska jag studera ditt script och se om jag kan förstå alla delar. Mitt första intryck är att anropet kan vara lite krångligt att komma ihåg i skallen.

dAEkMedlem sedan feb. 20041 816 inlägg
#8

På MDC finns det en implementation av Array.indexOf för webbläsare som saknar stöd för metoden: https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/indexOf#Compatibility

Nåja, du fick ett litet intro om hur man kan implementera egna namespaces åtminstone. :) Har man bara en funktion är det kanske overkill men man samlar ju på sig ett gäng funktioner förr eller senare och då är det ju trevligt att ha en sjyst struktur från början (tycker jag). YMMV.

stattinMedlem sedan jan. 2005713 inlägg
#9

dAEk skrev:

På MDC finns det en implementation av Array.indexOf för webbläsare som saknar stöd för metoden: https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/indexOf#Compatibility

Nåja, du fick ett litet intro om hur man kan implementera egna namespaces åtminstone. :) Har man bara en funktion är det kanske overkill men man samlar ju på sig ett gäng funktioner förr eller senare och då är det ju trevligt att ha en sjyst struktur från början (tycker jag). YMMV.

Självklart är det det, men först måste man ju förstå sig på den strukturen annars blir det ju rätt grötigt alla fall. Massa nytt för mig att ta in just nu.

Jaja nu ska jag lämna dator världen för ett tag, återkommer i augusti :)

Tack för hjälpen

dAEkMedlem sedan feb. 20041 816 inlägg
#10

Absolut. Det finns flera olika sätt att skapa hjälpklasser och det här är kanske inte det enklaste sättet om man inte är van vid js-syntax. Jag har ett gäng bokmärken ang. Javascript och klasser (samt OOP) om du vill ha mer info. De tar nog inte upp den här syntaxen om jag minns rätt.

Ingen annan som har bättre (dvs bra) tips ang. algoritmbiten?

149 ms totalt · 3 externa anrop · v20260731065814-full.30151723
0 ms — hämta forumlista (cache)
0 ms — hämta statistik (cache)
145 ms — hämta tråd, inlägg och bilagor (db)