Algoritam za izbor metode pri radu sa nizovima
Uz pomoć narednog snippeta koji je napisala Sarah Drasner, korišćenjem jednostavnog algoritma dolazimo do odgovarajuće metode:
See the Pen Array Explorer by Web programiranje (@chos) on CodePen.
Da li je ulaz niz?
Problem provere tipa podatka se zasniva na činjenici da se pri korišćenju metode toString() niz prebacuje u sledeći string: “[object Array]”.
1 2 3 4 5 6 7 8 9 |
function isArray (input) { if (toString.call(input) === "[object Array]"){ return true; } else { return false; } }; console.log(isArray('neki String')); // False console.log(isArray([1, 2, 4, 0])); // True |
Prečišćavanje elemenata niza
U sledećem snippet-u je prikazana funkcija koja eliminiše “nepoželjne” članove niza: null, “”, false, undefined i NaN.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function preciscavanje(niz) { var index = -1, arrLength = niz ? niz.length : 0, resIndex = -1, result = []; while (++index < arrLength) { var value = niz[index]; if (value || value === 0) { result[++resIndex] = value; } } return result; } console.log(preciscavanje([NaN, 0, 15, false, -22, '',undefined, 47, null])); // Vraca: [0, 15, -22, 47] |
Razlike u dva niza
U sledećem snippet-u prikazano rešenje nalazi sve članove niza koji se ne nalaze u oba niza.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
function razlikaNizova (array1, array2) { var temp = []; array1 = array1.toString().split(',').map(Number); array2 = array2.toString().split(',').map(Number); for (var i in array1) { if(array2.indexOf(array1[i]) === -1) { // temp.push(array1[i]); } } for(i in array2) { if(array1.indexOf(array2[i]) === -1) { temp.push(array2[i]); } } return temp.sort((a,b) => a-b); } console.log(razlikaNizova([1, 2, 3], [100, 2, 1, 10])); // Vraca: [3, 10, 100] |
Brisanje duplikatnih vrednosti u nizu
a) Niz primitiva
Prebacivanje niza u privremeni objekat (vrednosti niz u ključeve objekta), zatim vršiti iteracije kroz ključeve objekta sa operatorom in:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
function ukloniDuplikate(arr) { //Privremeni objekat: var temp = {}; for ( var i = 0; i < arr.length; i++){ temp[arr[i]] = true; } //Provera da li postoji u privremenom objektu: var r = []; for ( var k in temp){ r.push(k); } return r; } var niz = [ "Mika", 1, "Rale", "Pera", "Zika", 1, "Pera" ]; var bezDuplikata = ukloniDuplikate(niz); console.log(bezDuplikata); //Vraća: [1, "Mika", "Rale", "Pera", "Zika"] |
Ovaj pristup ne održava redosled i ne razlikuje broj od “broj” (1 == “1”)
1 2 3 |
var niz = [ "Mika", 1, "Rale", "Pera", "Zika", "1", "Pera" ]; var bezDuplikata = ukloniDuplikate(niz); console.log(jedinstvenaImena); //Vraća: ["1", "Mika", "Rale", "Pera", "Zika"] |
Ovde se koristi metoda filter() koja uzima callback funkciju kao argument. Callback funkcija testira svaki član niza (prema nekom uslovu), i ako je uslov ispunjen ubacuje (vraća) element u novi niz. Callback funkciji se automatski prosledjuju tri parametra currentValue, index, nizDuplikata.
U našem uslovu se koristi funkcija indexOf() koja pretražuje niz i nalazi index člana niza na osnovu njegove vrednosti. Ukoliko se indeks niza dobijen korišćenjem metode nizDuplikata.indexOf(currentValue) poklapa sa trenutnim indeksom to znači da nije bilo takve vrednosti. Ukoliko postoje dve iste vrednosti u nizu, pri pretrazi sa metodom indexOf() prva vrednost će da “prodje” uslov, dok druga neće jer će vratiti indeks od prve vrednosti, što se neće poklopiti sa trenutnim indeksom.
1 2 3 4 5 6 7 8 9 |
function ukloniDuplikate(nizDuplikata) { var ociscenNiz = nizDuplikata.filter(function (currentValue, index, nizDuplikata) { return nizDuplikata.indexOf(currentValue) === index; // USLOV }); return ociscenNiz; } var niz = [ "Mika", 1, "Rale", "Pera", 1, "Zika", "1", "Pera" ]; var bezDuplikata = ukloniDuplikate(niz); console.log(bezDuplikata); // Vraća: ["Mika", 1, "Rale", "Pera", "Zika", "1"] |
Set je nova vrsta kolekcije podataka koja se pojavila sa ES6. Glavna karakteristika ove kolekcije je da sadži samo jedinstvene vrednosti u kolekciji.
1 2 3 4 5 6 7 8 9 10 11 |
function ukloniDuplikate(niz) { let setObjekat = new Set(niz); let sredjenNiz=[]; for(let element of setObjekat){ sredjenNiz.push(element); } return sredjenNiz; } var niz = [ "Mika", 1, "Rale", "Pera", 1, "Zika", "1", "Pera" ]; var bezDuplikata = ukloniDuplikate(niz); console.log(bezDuplikata); // Vraća: ["Mika", 1, "Rale", "Pera", "Zika", "1"] |
Postupak:
Prvo se napravi set objekat koji ima jedinstvene vrednosti, zatim se sa for..of iteracijom kroz set objekat napravi niz. Ovaj postupak je mogao da se “skrati” korišćenjem ES6 metode from() koja pravi niz:
1 2 3 4 5 6 7 8 |
function ukloniDuplikate(niz) { let setObjekat = new Set(niz); let sredjenNiz = Array.from(setObjekat); return sredjenNiz; } var niz = [ "Mika", 1, "Rale", "Pera", 1, "Zika", "1", "Pera" ]; var bezDuplikata = ukloniDuplikate(niz); console.log(bezDuplikata); // Vraća: ["Mika", 1, "Rale", "Pera", "Zika", "1"] |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function ukloniDuplikate(nizDuplikata){ var ociscenNiz = []; $.each(nizDuplikata, function(i, currentValue){ if($.inArray(currentValue, ociscenNiz) === -1) { ociscenNiz.push(currentValue); } }); return ociscenNiz; } var niz = [ "Mika", 1, "Rale", "Pera", 1, "Zika", "1", "Pera" ]; var bezDuplikata = ukloniDuplikate(niz); console.log(bezDuplikata); // Vraća: ["Mika", 1, "Rale", "Pera", "Zika", "1"] |
b) Niz objekata
Za razliku od primitiva koji se čuvaju prema vrednosti, objekti se čuvaju prema referenci, stoga prethodni pristupi neće dati dobre rezultate.
1 2 3 |
1 === 1 // true 'a' === 'a' // true { a: 1 } === { a: 1 } // false |
Za pronalaženje duplikata niza koristićemo metodu filter() koja pravi novi niz od elemenata niza koji su prošli neki test i vratili TRUE. Pri punjenju pomoćnog objekta sa parovima key/value (ključ/vrednost), svaki ključ se konvertuje u string, stoga ne možemo da razlikujemo broj 1 od string-a “1”. Ali korišćenje izraza JSON.stringify(el) nam omogućava da se ipak razlikuju. Ovaj izraz vraća različite vrednosti u zavisnosti od tipa, što se vidi na sledećem primeru:
1 2 |
pomocniObjekat[JSON.stringify(1)] // '1' pomocniObjekat[JSON.stringify('1')] // '\'1\' |
1 2 3 4 5 6 7 8 9 10 11 12 |
function ukloniDuplikate(nizDuplikata) { var pomocniObjekat = {}; return nizDuplikata.filter(function (el) { var key = JSON.stringify(el); var match = Boolean(pomocniObjekat[key]); return (match ? false : pomocniObjekat[key] = true); }); } var nizObjekata = [ { a: 1 }, { a: 1 }, [ 1, 2 ], [ 1, 2 ], 1, 1, "1", "1" ] var bezDuplikata = ukloniDuplikate(nizObjekata); console.log(bezDuplikata); // [ {a: 1}, [1, 2], 1, "1"] |
Spajanje dva niza
Spajanje dva niza (sa duplikatima)
Spajenje dva niza u novi niz sa metodom concat() generiše novi niz. Medjutim ova metoda nije dobra kod velikih nizova jer troši resurse kreiranjem novog niza.
1 2 3 4 |
var array1 = [1, 2, 3]; var array2 = [4, 5, 6]; var array3 = array1.concat(array2); console.log(array3); // Vraća: [1,2,3,4,5,6] |
Metoda push() ne generiše novi niz, pa je zbog toga štedljivija po pitanju resursa u odnosu na metodu concat(), pa je ova metoda preporučena u slučaju rada sa velikim nizovima. Medjutim ovaj način ima ogranjičenje, niz koji se dodaje (u ovome slučaju array2) ne može da ima veći broj članova od maksimalnog broj parametara koje funkcija može da primi (Chrome33: 65535, Firefox27:262143, IE11: 131071, Opera12: 1048576).
1 2 3 4 |
var array1 = [1, 2, 3]; var array2 = [4, 5, 6]; array1.push.apply(array1, array2); console.log(array1); // Vraća: [1,2,3,4,5,6] |
ili
1 2 3 4 |
var array1 = [1, 2, 3]; var array2 = [4, 5, 6]; Array.prototype.push.apply(array1, array2); console.log(array1); // Vraća: [1,2,3,4,5,6] |
Napomena: Pogledajte ovde zbog čega se koristi apply() metoda u prethodnim izrazima.
Spajanje dva niza je jednostavno korišćenjem spread opteratora:
1 2 3 4 |
var array1 = [1, 2, 3]; var array2 = [4, 5, 6]; var array3 = [...array1, ...array2]; console.log(array3); // Vraća: [1,2,3,4,5,6] |
Spajanje dva niza (bez duplikata)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
function spajanjeNizova(array1, array2) { var krajnjiNiz = []; var arr = array1.concat(array2); var len = arr.length; var assoc = {}; while(len--) { var item = arr[len]; if(!assoc[item]) { krajnjiNiz.unshift(item); assoc[item] = true; } } return krajnjiNiz; } var array1 = [1, 2, 3]; var array2 = [2, 30, 1]; console.log(spajanjeNizova(array1, array2)); // Vraca: [3, 2, 30, 1] |
Pravljenje klona niza
Ukoliko želimo da napravimo novi niz koji će da zauzme novo mesto u memoriji pri čemu će da sadrži kopiju nekog niza u ovom trenutku, koristićemo sledeći snippet:
1 2 3 4 5 6 7 |
function kloniranje (niz) { var noviNiz = niz.slice(0); return noviNiz; }; var original = [5,4,3]; var novi = kloniranje(niz); |
Dobijanje poslednjih članova niza
Poslednji članovi niza se dobijaju koristeći metodu slice() ali sa negativnim argumentom -1:
1 2 3 4 |
var array = [1, 2, 3, 4, 5, 6]; console.log(array.slice(-1)); // [6] console.log(array.slice(-2)); // [5,6] console.log(array.slice(-3)); // [4,5,6] |
Pronalaženje člana niza prema vrednosti
U sledećem snippet-u se koristi metoda indexOf() koja vraća index traženog elementa koji se nalazi u nizu ili vraća vrednost -1 ako ga niz ne sadrži. Ukoliko se u metodi indexOf() koriste dva atributa onda drugi atribut definiše od kog index-a da se počne pretraga.
1 2 3 4 5 6 7 8 9 |
function pretraziNiz(element, niz) { var nadjeniClanovi = []; var indexNadjenog = niz.indexOf(element); while (indexNadjenog != -1) { nadjeniClanovi.push(indexNadjenog); indexNadjenog = niz.indexOf(element, indexNadjenog + 1); // NAPOMENA: drugi atribut definiše od kog index-a da se počne pretraga } console.log("Ovo su indeksi pojavljivanja elementa: ", nadjeniClanovi); } |
Primer
1 2 |
nekiNiz = ['a', 'b', 'a', 'c', 'a', 'd']; pretraziNiz("a", nekiNiz); // Vraća: [0, 2, 4] |
Uklanjanje odredjenog člana niza
Uklanjanje odredjenog člana niza izabranog prema vrednosti člana:
1 2 3 4 5 6 7 8 9 10 |
function izbaciVrednost(niz, vrednost) { for(var i=0; i < niz.length; i++) { if(niz[i] == vrednost) { niz.splice(i, 1); break; } } } var nekiNiz = ["ponedeljak", "utorak", "sreda", "cetvrtak", "petak"]; izbaciVrednost(nekiNiz, "utorak"); // Vraca: ["ponedeljak", "sreda", "cetvrtak", "petak"] |
Pražnjenje niza
1 2 |
var list = [1, 2, 3, 4]; list.length = 0; |
Kod ovog načina treba obratiti pažnju da se na ovaj način briše sadržaj memorije na koju referenca te promenjive ukazuje, pa ako postoji još neka promenjiva koja ima istu referencu na taj deo memorije i kod nje će doći do izmene.
1 2 3 4 |
var niz = [1, 2, 3, 4]; var drugiNiz = niz; niz.length = 0; console.log(drugiNiz); // Vraca: [] |
Ovaj način koristimo ukoliko želimo da obrišemo članove niza ali da ne obrišemo referencu. Izraz “niz=[]” dodeljuje novi prazan niz promenjivoj i ne utiče na druge reference. Treba napomenuti da ovaj način troši memoriju jer je mesto u memoriji gde je čuvan stari niz još uvek zauzeto starim podacima o nizu.
1 2 3 4 |
var niz = [1, 2, 3, 4]; var drugiNiz = niz; niz = []; console.log(drugiNiz); // Vraca: [1, 2, 3, 4] |
Sortiranja
Sortiranje niza stringova
1 2 |
var fruit = ['cherries', 'apples', 'bananas']; fruit.sort(); // ['apples', 'bananas', 'cherries'] |
Sortiranje liste
See the Pen JS sort list by Web programiranje (@chos) on CodePen.
See the Pen Sortiranje liste jQuery by Web programiranje (@chos) on CodePen.
Sortiranje niza brojeva
Metoda sort()je inicijalno namenjena za sortiranje niza stringova, stoga pri sortiranju niza brojeva daje pogrešne rezultate.
1 2 |
//Sortiranje brojeva kao što se sortiraju stringovi daje pogrešan rezultat: [10,1, 5].sort() // [1, 10, 5] |
Rešenje
Za korišćenje sa brojevima je potrebno dodati funkciju poredjenja.
1 2 3 4 5 |
// Sortiranje brojeva sa ES5 nekiNiz = nekiNiz.sort(function (a, b) { return a - b; }); // Sortiranje brojeva sa ES2015 nekiNiz = nekiNiz.sort((a, b) => a - b); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
function Selection_Sort(arr, compare_Function) { function compare(a, b) { return a - b; } var min = 0; var index = 0; var temp = 0; compare_Function = compare_Function || compare; for (var i = 0; i < arr.length; i += 1) { index = i; min = arr[i]; for (var j = i + 1; j < arr.length; j += 1) { if (compare_Function(min, arr[j]) > 0) { min = arr[j]; index = j; } } temp = arr[i]; arr[i] = min; arr[index] = temp; } return arr; } console.log(Selection_Sort([3, 0, 2, 5, -1, 4, 1], function(a, b) { return a - b; })); console.log(Selection_Sort([3, 0, 2, 5, -1, 4, 1], function(a, b) { return b - a; })); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
function quick_Sort(origArray) { if (origArray.length <= 1) { return origArray; } else { var left = []; var right = []; var newArray = []; var pivot = origArray.pop(); var length = origArray.length; for (var i = 0; i < length; i++) { if (origArray[i] <= pivot) { left.push(origArray[i]); } else { right.push(origArray[i]); } } return newArray.concat(quick_Sort(left), pivot, quick_Sort(right)); } } var myArray = [3, 0, 2, 5, -1, 4, 1 ]; console.log("Original array: " + myArray); var sortedArray = quick_Sort(myArray); console.log("Sorted array: " + sortedArray); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
function shellSort(arr) { var increment = arr.length / 2; while (increment > 0) { for (i = increment; i < arr.length; i++) { var j = i; var temp = arr[i]; while (j >= increment && arr[j-increment] > temp) { arr[j] = arr[j-increment]; j = j - increment; } arr[j] = temp; } if (increment == 2) { increment = 1; } else { increment = parseInt(increment*5 / 11); } } return arr; } console.log(shellSort([3, 0, 2, 5, -1, 4, 1])); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
function insertion_Sort(arr){ for (var i = 1; i < arr.length; i++) { if (arr[i] < arr[0]) { //move current element to the first position arr.unshift(arr.splice(i,1)[0]); } else if (arr[i] > arr[i-1]) { //leave current element where it is continue; } else { //find where element should go for (var j = 1; j < i; j++) { if (arr[i] > arr[j-1] && arr[i] < arr[j]) { //move element arr.splice(j,0,arr.splice(i,1)[0]); } } } } return arr; } console.log(insertion_Sort([3, 0, 2, 5, -1, 4, 1])); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
function swap(arr, first_Index, second_Index){ var temp = arr[first_Index]; arr[first_Index] = arr[second_Index]; arr[second_Index] = temp; } function bubble_Sort(arr){ var len = arr.length, i, j, stop; for (i=0; i < len; i++){ for (j=0, stop=len-i; j < stop; j++){ if (arr[j] > arr[j+1]){ swap(arr, j, j+1); } } } return arr; } console.log(bubble_Sort([3, 0, 2, 5, -1, 4, 1])); |
Max i min niza
Najmanji član niza
ES5 način
1 2 3 4 5 6 |
function minNiza(niz) { if (toString.call(niz) !== "[object Array]") { return false; } return Math.min.apply(null, niz); } |
ES6 – spread operator
1 2 3 4 5 6 |
function minNiza (niz){ if (toString.call(niz) !== "[object Array]") { return false; } return Math.min(...niz); } |
ES6 – Spread operator + arrow funkcija
ili još preglednije napisano sa arrow funkcijom:
1 2 3 |
const minNiza = niz => Math.min(...niz); // Primer minNiza([20, 10, 5, 10]) // Vraća: 5 |
Najveći član niza
1 |
const maxNiza = niz => Math.max(...niz); |
Suma niza
1 2 |
const sumaNiza = niz => niz.reduce((a,b) => a + b, 0) sumaNiza([20, 10, 5, 10]) -> 45 |
Prosečni član niza
1 2 |
const prosekNiza = niz => niz.reduce((a,b) => a + b, 0) / niz.length prosekNiza([20, 10, 5, 10]) -> 11.25 |
Mešanje redosleda elemenata niza
Metoda sort() uzima za parametar callback funkciju koja “funkcija za poredjenje” (ili negativan broj ili nulu ili pozitivan broj), metoda sort() zna koji je broj veći, a koji je manji. Stoga ako “zeznemo” sort() metodu i vraćamo joj slučajne rezultate i ona će da vrati slučajno izmešane brojeve niza.
1 2 3 4 5 |
var list = [1, 2, 3, 4, 5, 6]; list.sort(function() { return Math.random() - 0.5 }); console.log (list); //Vraca svaki put drugi raspored |
Ovaj način koristi Fisher–Yates algoritam:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function shuffle(arr) { var i, j, temp; for (i = arr.length - 1; i > 0; i--) { j = Math.floor(Math.random() * (i + 1)); temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } return arr; }; var a = [1, 2, 3, 4, 5, 6, 7, 8]; var a = shuffle(a); // Vraca: niz sa promesanim elementima |
Izvlačenje random člana niza
1 2 3 4 5 6 |
function randomClan(niz){ return niz[Math.floor(Math.random()*niz.length)]; } var niz = [254, 45, 212, 365, 2543]; console.log(randomClan(items)); //Vraća svaki put drugi član |
Prebacivanje niza u listu elemenata
Neke ugradjene funkcije u JavaScript-u npr. Math.max() ne prihvataju niz kao argument već listu. Stoga se javlja potreba da transformišemo niz u listu, a to možemo uraditi na sledeće načine:
Funkcija apply() može da prihvati niz kao argument, pa iako joj to nije glavna namena, često se koristi da “pretvori” niz u listu argumenata. Prvi argument apply() funkcije mora da bude objekat na koji ukazuje this ključna reč (ovo je primarna namena funkcije apply), a drugi je niz koji se prosledjuje callback funkciji kao lista argumenata.
Primer
U sledećem primeru funkcija Math objekta prihvata samo listu argumenata (ne i niz), pa se pre nje koristi apply():
1 2 3 4 5 |
var numbers = [5, 6, 2, 3, 7]; console.log(Math.max(numbers)); // Vraća: NaN jer je objektu Math prosledjen niz a ne lista Math.max.apply(Math, numbers) == Math.max(5, 6, 2, 3, 7); // TRUE |
Primer
Metodi push() je potrebno da se prosledjuje lista elemenata u vidu alrgumenata, kao na sledećem primeru:.
1 2 |
var numbers = [1, 2, 3]; numbers.push(4, 5, 6); // Vraća: [1, 2, 3, 4, 5, 6] |
Medjutim ako želimo da prosledimo ceo niz, potrebno je da ka prosledimo kao listu, a za to ćemo koristi apply() metodu koja očekuje kao drugi argument listu, pa će implicitno prebaciti niz u listu:
1 2 3 4 |
var array1 = [1, 2, 3]; var array2 = [4, 5, 6]; Array.prototype.push.apply(array1, array2); console.log(array1); // Vraća: [1, 2, 3, 4, 5, 6] |
Ovaj način zahteva korišćenje ES6 spread operator-a:
1 2 |
var numbers = [5, 6, 2, 3, 7]; var max = Math.max(...numbers); // Vraća najveći broj |