Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Перебор массива - вложенный цикл (https://javascript.ru/forum/misc/43832-perebor-massiva-vlozhennyjj-cikl.html)

SWin 23.12.2013 10:39

Перебор массива - вложенный цикл
 
Не могу понять где ошибки.
Задача:
необходимо написать функцию, которая проверяла - содержаться ли элементы массива array 2 в массиве array 1 и в зависимости от этого возвращала булевое значение.

Решение (неработающее):
Создаю два цикла:
-внешний цикл перебирает значения array1 и присваивает их var arrayElem, которая передаётся во внутренний цикл;
-во внутреннем цикле перебираются значения array2 и сравниваются с var arrayElem;
-если значения равны то array2[j] помещаеться в пустой массив matchArray;
-если длина массива matchArray === длине массива array2 возвращаеться true, иначе false.

Код:

function contains(array1, array2) {
        "use strict";
        array1 = [1, 2, 3, 4, 5];
        what = [1, 2, 3];
        var matchArray = [];

        for (var i = 0; i < array1.length; i++) {
                var arrayElem = array1[i];
                for (var j = 0; j < what.length; i++) {
                        if (what[j] === arrayElem) {
                                matchArray.push(what[j]);
                        }
                }
        }
        if (matchArray.length === what.length) {
                return true;
        } else {
                return false;
        }
}


ksa 23.12.2013 12:00

Цитата:

Сообщение от SWin
необходимо написать функцию, которая проверяла - содержаться ли элементы массива array 2 в массиве array 1 и в зависимости от этого возвращала булевое значение.

Как вариант...

var a1=[1,2,3,4,5];
var a2=[2,4];
alert(cross(a1,a2));
function cross(Ar1,Ar2) {
	for (var i=0; i<Ar1.length; i++) {
		for (var j=0; j<Ar2.length; j++) {
			if (Ar1[i]==Ar2[j]) {
				return true;
			};
		};
	};
	return false;
};

danik.js 23.12.2013 12:50

ksa, то есть при первом же совпадении считаем остальные элементы тоже совпавшими? Странная логика..

danik.js 23.12.2013 13:07

Используя ES5 функционал:
var a1 = [1,2,3,4,5];
var a2 = [2,4];

alert(isSubset(a1, a2));

function isSubset(a1, a2) {
    return a2.every(function(element) {
        return a1.indexOf(element) !== -1;
    });
}
;

danik.js 23.12.2013 13:09

Цитата:

Сообщение от SWin
-если длина массива matchArray === длине массива array2 возвращаеться true, иначе false.

Зачем вобще тогда наполнять массив matchArray, если он в дальнейшем не используется, а требуется всего лишь длина (количество совпадений)? Может тогда просто считать эти самые совпадения?

ksa 23.12.2013 13:21

Цитата:

Сообщение от danik.js
Странная логика..

Я почему-то решил, что автору достаточно одного совпадения...
Если второй массив должен быть частью первого - тогда все сложнее. Ведь может получиться и такая комбинация

var a1=[1,2,3,4,5];
var a2=[2,4,4,2];

И как тогда?

SWin 23.12.2013 13:37

Цитата:

Сообщение от danik.js (Сообщение 288530)
Зачем вобще тогда наполнять массив matchArray, если он в дальнейшем не используется, а требуется всего лишь длина (количество совпадений)? Может тогда просто считать эти самые совпадения?

Да, так логичнее.
function contains(array1, array2) {
	"use strict";
	array1 = [1, 2, 3, 4, 5];
	what = [1, 2, 3];
	var matchArray = [];
        var counter = 0;

	for (var i = 0; i < array1.length; i++) {
		var arrayElem = array1[i];
		for (var j = 0; j < what.length; i++) {
			if (what[j] === arrayElem) {
				counter = counter + 1;
			}
		}
	}
	if (counter === what.length) {
		return true;
	} else {
		return false;
	}
}

К сожалению, мне как начинающему не совсем понятен Ваш код.

Забыл сказать: я не могу в коде использовать те элементы JS, которые мы ещё не изучали а до indexOf еще не добежали.

danik.js 23.12.2013 13:39

Цитата:

Сообщение от ksa
И как тогда?

var a1=[1,2,3,4,5];
var a2=[2,4,4,2];

alert(isSubset(a1, a2));

function isSubset(a1, a2) {
    for (var i = 0; i < a2.length; i++) {
        var element = a2[i];
        var found = false;
        for (var j = 0; j < a1.length; j++) {
            if (a1[j] === element) {
                found = true;
                break;
            }
        }
        if (!found) {
            return false;
        }
    }
    return true;
}

ksa 23.12.2013 13:45

Цитата:

Сообщение от danik.js (Сообщение 288546)
var a1=[1,2,3,4,5];
var a2=[2,4,4,2];

alert(isSubset(a1, a2));

function isSubset(a1, a2) {
    for (var i = 0; i < a2.length; i++) {
        var element = a2[i];
        var found = false;
        for (var j = 0; j < a1.length; j++) {
            if (a1[j] === element) {
                found = true;
                break;
            }
        }
        if (!found) {
            return false;
        }
    }
    return true;
}

Этот вариант возвращает true... Хотя второй массив не часть первого. :no:

danik.js 23.12.2013 13:53

Цитата:

Сообщение от ksa
Хотя второй массив не часть первого.

А что по твоему означает "второй массив" - часть первого?
Я не зря назвал функцию isSubset. В множествах нет повторяющихся элементов, поэтому [2, 4, 4, 2] - тоже самое что и [2, 4], и в множествах порядок не имеет значения. Именно так функция и работает - игнорирует порядок и повторы.

ksa 23.12.2013 13:55

Цитата:

Сообщение от danik.js
А что по твоему означает "второй массив" - часть первого?

Это обычное понятие "быть частью чего-либо"... Т.е. это не какое-то мое понятие... Это понятие вообще.

Например в первом массиве нет двух 2, как и двух 4... Т.о. второй массив ну никак не может быть частью первого.

ksa 23.12.2013 13:56

Цитата:

Сообщение от danik.js
Именно так функция и работает - игнорирует порядок и повторы.

Это-то понятно... :D

danik.js 23.12.2013 14:07

ksa, тогда предлагаю такой вариант:
Сначала отсортировать a2, далее при переборе хранить найденный индекс совпадающего элемента в a1, сверять текущий элемент с предыдущим элементом, и если они равны, то искать дубль уже с сохраненного индекса, а не с начала.

Vlasenko Fedor 23.12.2013 14:08

var a1 = [1, 2, 3, 4, 5];
      var a2 = [2, 4, 4, 2];
      var buf = {},
        rez = false,
        i;
      for (i = 0; i < a2.length; i++) {
        buf[a2[i]] = true;
      }
      for (i = 0; i < a1.length; i++) {
        if (buf[a1[i]]) {
          rez = true;
          break;
        }
      }
      alert(rez);

то же самое http://javascript.ru/forum/misc/4366...ny-stroki.html

ksa 23.12.2013 14:08

Цитата:

Сообщение от danik.js
тогда предлагаю такой вариант ...

Это пусть уже автор думает, что ему делать... :D

ksa 23.12.2013 14:11

Цитата:

Сообщение от Poznakomlus (Сообщение 288571)
var a1 = [1, 2, 3, 4, 5];
      var a2 = [2, 4, 4, 2];
      var buf = {},
        rez = false,
        i;
      for (i = 0; i < a2.length; i++) {
        buf[a2[i]] = true;
      }
      for (i = 0; i < a1.length; i++) {
        if (buf[a1[i]]) {
          rez = true;
          break;
        }
      }
      alert(rez);

то же самое http://javascript.ru/forum/misc/4366...ny-stroki.html

Твой вариант не пройдет, по описаным выше причинам... :no:

ksa 23.12.2013 14:16

Цитата:

Сообщение от danik.js
Сначала отсортировать a2, далее при переборе хранить найденный индекс совпадающего элемента в a1, сверять текущий элемент с предыдущим элементом, и если они равны, то искать дубль уже с сохраненного индекса, а не с начала.

А как тебе такой вариант:
- переделать первый массив в объект (o[a[i]]=<количество_елементов>)
- пройтись по второму массиву проверяя и "минусуя" количество
- если проверка и декремент не выявил патологии - таки является частью.
Т.о. 1 проход по первому и 1 проход по второму - вроде не так уж и плохо получится... :)

ksa 23.12.2013 14:26

Цитата:

Сообщение от Poznakomlus
мой пример является решением, этой задачи.

Нет. :no:
Цитата:

Сообщение от Poznakomlus
Не кажется, что вы усложняете задачу не описанную Т.С.

Нет. :no:

Т.к. массив
var a2 = [2, 4, 4, 2];

не является частью массива
var a1 = [1, 2, 3, 4, 5];

Vlasenko Fedor 23.12.2013 14:32

что вы считаете частью массива?
не нашел у Т. С. такого
a2 [2, 3] ?
a3 [3, 2] ?
a4 [3] ?
массива a1 = [1, 2, 3, 4, 5];

ksa 23.12.2013 14:41

Цитата:

Сообщение от Poznakomlus
что вы считаете частью массива?

Дабы не повторяться...
http://javascript.ru/forum/misc/4383...tml#post288560

Цитата:

Сообщение от Poznakomlus
не нашел у Т. С. такого

Ну не нашел - так не нашел... :D

Вот мой вариант на проверку именно части одного массива в другом

var a1 = [1, 2, 3, 4, 5];
var a2 = [2, 4, 4, 2];
alert(part(a1,a2));
a2 = [2, 4];
alert(part(a1,a2));
function part(Ar1,Ar2) {
	var o={};
	for (var i=0; i<Ar1.length; i++) {
		o[Ar1[i]]=(o[Ar1[i]]||0)+1;
	};
	for (i=0; i<Ar2.length; i++) {
		if (o[Ar2[i]]) {
			if (o[Ar2[i]]--==0) {
				return false;
			};
		} else {
			return false;
		}; 
	};
	return true;
};

SWin 23.12.2013 23:00

danik.js, бы вы вкратце рассказать о порядке выполнения кода.
Моих более чем скромных знаний JS не хватает.

Насколько я понимаю при нахождении совпадающего элемента массива
a1[j] === element вложенный цикл прерываеться командой break, при этом found = true и выполнениние функции продолжается во внешнем цикле со строки for (var i = 0; i < a2.length; i++).
При этом значение found снова становиться false и дальше снова переходим во внутренний цикл и так по кругу.
Правильно?
Что происходит дальше. Не совсем понятны строки ниже 15-ой.

Цитата:

Сообщение от danik.js (Сообщение 288546)
var a1=[1,2,3,4,5];
var a2=[2,4,4,2];

alert(isSubset(a1, a2));

function isSubset(a1, a2) {
    for (var i = 0; i < a2.length; i++) {
        var element = a2[i];
        var found = false;
        for (var j = 0; j < a1.length; j++) {
            if (a1[j] === element) {
                found = true;
                break;
            }
        }
        if (!found) {
            return false;
        }
    }
    return true;
}


danik.js 24.12.2013 00:33

Цитата:

Сообщение от SWin
Что происходит дальше. Не совсем понятны строки ниже 15-ой.

Ну дык если какой-то элемент из a2 не нашли в a1, то какой смысл проверять остальные элементы?

Vlasenko Fedor 24.12.2013 03:07

А это иллюстрация того, что выбрано не правильное решение.
var a1 = [1, 2, 3, 4, 5];
      var a2 = [2, 4, 4, 2];
      var b = 0;

      alert(isSubset(a1, a2));
      alert(b);

      function isSubset(a1, a2) {
        for (var i = 0; i < a2.length; i++) {
          var element = a2[i];
          var found = false;
          for (var j = 0; j < a1.length; j++) {
            b++;
            if (a1[j] === element) {
              found = true;
              break;
            }
          }
          if (!found) {
            return false;
          }
        }
        return true;
      }

и предложное мной
var a1 = [1, 2, 3, 4, 5];
      var a2 = [2, 4, 4, 2];
      var buf = {},
        rez = false,
        i, b = 0;
      for (i = 0; i < a2.length; i++) {
        buf[a2[i]] = true;
        b++;
      }
      for (i = 0; i < a1.length; i++) {
        b++;
        if (buf[a1[i]]) {
          rez = true;
          break;
        }
      }
      alert(rez);
      alert(b);

Vlasenko Fedor 24.12.2013 04:14

Немного оптимизировал
var a1 = [1, 2, 3, 4, 5];
var a2 = [2, 4, 4, 2];
      var comparing = function (ar1, ar2) {
          var a, b, i, buf = {};
          ar1.length < ar2.length ? (a = ar1, b = ar2) : (a = ar2, b = ar1);
          for (i = 0; i < a.length; i++)  buf[a[i]] = 1;
          for (i = 0; i < b.length; i++) {
            if(buf[b[i]]) return true;
          }
          return false;
        }
alert(comparing(a1, a2));

алгоритм такой
1. Сравниваем длину массивов
2. Из меньшего делаем объект(алфавит) с оригинальными значениями. При таком подходе мы быстрее переходим непосредственно к нахождению первого вхождения. Но, это как бабка надвое гадала может в конечном результате привести к увеличению операций для поиска. В некоторых ситуациях следовало из массивов делать 2 объекта с оригинальными значениями. И сравнивать уже их.
3. Соответственно поиск совпадающего значения
http://javascript.ru/php/array_diff_key
http://javascript.ru/php/array_diff
Как вариант компактного
var a1 = [1, 2, 3, 4, 5];
var a2 = [2, 4, 4, 2];

var comparing = function (a, b) {
	var i, buf = {};
	for (i = 0; i < a.length; i++) buf[a[i]] = 1;
	for (i = 0; i < b.length; i++) if (buf[b[i]]) return true;
	return false;
};

alert(comparing(a1, a2));

Vlasenko Fedor 24.12.2013 18:19

Вариант №2 Поиск совладения значений массива в любом порядке в искомом массиве

var a1 = [1, 2, 1, 3, 4, 5, 1, 9];
var a2 = [1, 5];
var a3 = [5, 1]; 
var a4 = [5, 1, 1, 3, 7]; 
    
var comparing = function (a, b) {
	
	if (a.length < b.length) return false;
	var i, buf = {}, maxel =  b.length - 1;
	for (i = 0; i < a.length; i++) buf[a[i]] = (buf[a[i]] || 0) + 1;
	for (i = 0; ;) {
		if (!buf[b[i]]--) return false;
		if (i++ == maxel) return true;
	}
}

alert(comparing(a1, a2));
alert(comparing(a1, a3));
alert(comparing(a1, a4));

Здесь максимальная длина проверки a.length + b.length

Vlasenko Fedor 25.12.2013 01:15

okouser,
тестируйте на примерах. Буфер нужен для уменьшения количеств итераций. И как раз он и служит для увеличения скорости
В твоем варианте максимальная длина проверки a.length * b.length
Вот тебе и буфер

Vlasenko Fedor 25.12.2013 01:51

Цитата:

Сообщение от okouser (Сообщение 289088)
В чем проблема?
Я привел решения задачи для трех случаев:
2) когда нужно проверить произвольное вхождение всех элементов

У вас последовательное, не произвольное
arr =[5, 1] и arr[1, 5] разный результат

Vlasenko Fedor 25.12.2013 02:35

var a1 = [1, 2, 3, 4, 5];
var a2 = [1, 5];
var a2 = [5, 1];
При таких значениях

Vlasenko Fedor 25.12.2013 04:39

Вариант №1 Возвращает false или индекс элемента с которого начинается совпадение
Да первый вариант стоит того. Не знаю может кто красивее напишет
var a1 = [2, 5, 2, 4];
      var a2 = [2, 4];
      var a3 = [3, 4];
      var a4 = [5];

      alert(isSubset(a1, a2));
      alert(isSubset(a1, a3));
      alert(isSubset(a1, a4));

      function isSubset(a1, a2) {
        var i = 0,
          j = 1,
          ind = 0,
          maxid = a2.length - 1;

        for (; i < a1.length; i++) {
          if (a1.length - i == maxid) return false;
          if (a1[i] != a2[0]) continue;
          ind = i;
          if (maxid == 0) return ind;
          
          for (; j < a2.length; j++) {
            if (a1.length - i == maxid) return false;
            if (a2[j] != a1[i+1]) break;
            if (j == maxid) return ind;
          }

        }
        return false;
      };

Вариант 3. Самый простой. Цикл внутри цикла. Находит единственное совпадение
var a1 = [2, 5, 2, 4];
      var a2 = [7, 8];
      var a3 = [3, 4];
      var a4 = [5];

      alert(isSubset(a1, a2));
      alert(isSubset(a1, a3));
      alert(isSubset(a1, a4));

      function isSubset(a1, a2) {
        for (var i = 0; i < a2.length; i++) {
          for (var j = 0; j < a1.length; j++) {
            if (a2[i] == a1[j]) return true;
          }
        }
        return false;
      };

Vlasenko Fedor 25.12.2013 12:13

Я написал так, к тому, что в рассмотренных здесь вариантах не были применены технологии прироста производительности.
К примеру изменение порядка цикла на обратный
var i = arr.length - 1;
while(i--){}

Устройства Даффа и других.
Метод написанный и не оттестированный - это всего лишь предположение, далекое от жизни. Честно, я и сейчас вижу как это можно написать лучше.:)

ksa 25.12.2013 16:17

Какая жизненная тема оказалась! :D

Vlasenko Fedor 26.12.2013 21:39

Цитата:

Сообщение от okouser
3) когда достаточно найти лишь вхождение одного элемента из подмассива.

Новый Вариант №3. Хорошая тема,:)
var a1 = [2, 5, 2, 4, 5, 11, 5, 9, 9];
var a2 = [2, 7, 8, 6, 1, 1];
var a3 = [3, 1];
var a4 = [5];

alert(isValueInArray(a1, a2));
alert(isValueInArray(a1, a3));
alert(isValueInArray(a1, a4));

function isValueInArray(a1, a2) {

    var i = 0,
        j = 0,
        sl, el,
        l1 = a1.length,
        l2 = a2.length,
        len = l1 % 8,
        slen = Math.floor(l1 / 8);

    for (; i < l2; j = 0) {
        
        el = a2[i++];
      
        for (; 0 < len; len--) {
            if (el == a1[j++]) return true;
        }

        for (sl = slen; 0 < sl; sl--) {
            if (
                el == a1[j++] ||
                el == a1[j++] ||
                el == a1[j++] ||
                el == a1[j++] ||
                el == a1[j++] ||
                el == a1[j++] ||
                el == a1[j++] ||
                el == a1[j++])
                return true;
        }

    }
    return false;
}

Vlasenko Fedor 27.12.2013 03:53

Спасибо за ссылку плюсанул
http://jsperf.com/isvalueinarray
не знал, что while на 25% медленный
и по ходу на больших массивах for впереди

Antonius 27.12.2013 04:39

Poznakomlus,
Я правильно понимаю, что основная идея такой оптимизации лежит в сокращении количества итераций цикла за счет проверки группами?

Не уверен что правильно разобрал алгоритм.

Vlasenko Fedor 27.12.2013 04:51

Устройство Даффа,
но увы, это не приносит эффекта а еще и усугубляет
цикл фор рулит

Antonius 27.12.2013 05:06

Так, что-то сегодня или со мной, или не знаю с чем.

Уже второй раз сначала получаю неправильные результаты замеров производительности. Когда впервые запустил пример кода по вашей ссылке, получилось, что оптимизированный вариант был почти вдвое быстрее. Теперь не пойму, куда я смотрел или что это было.

А запись исходной реализации на C после долгого отсутствия практики на нем взрывает мозг.


Часовой пояс GMT +3, время: 13:19.