Что нового

[Автоматизация] Сравнение элементов одномерного массива

classick

Новичок
Сообщения
16
Репутация
1
Добрый день.

Уже который день мучаюсь над тем чтобы сравнить все элементы одномерного массива и вывести в 2-й (результирующий) массив значения, которые не повторяются.

К примеру, у меня есть массив:
Код:
Local $a[11]
$a[0] = 1
$a[1] = 1
$a[2] = 2
$a[3] = 2
$a[4] = 3
$a[5] = 5
$a[6] = 1
$a[7] = 6
$a[8] = 7
$a[9] = 8
$a[10] = 8

В результате, я хочу получить массив из элементов, которые встречаются только один раз, а именно:
Код:
$a[0] = 5
$a[1] = 3
$a[2] = 5
$a[3] = 6
$a[4] = 7
Помогите, пожалуйста, т.к. поиск вывел меня лишь на функцию _ArrayUnique, но это не совсем то, что нужно, т.к. просто удаляет дубли.

Спасибо.

Используйте для AutoIt кода тег [autoit]
autoit.gif

madmasles.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
classick,
Как вариант.
Код:
#include <Array.au3>

Opt('MustDeclareVars', 1)

Local $a[11]
$a[0] = 1
$a[1] = 1
$a[2] = 2
$a[3] = 2
$a[4] = 3
$a[5] = 5
$a[6] = 1
$a[7] = 6
$a[8] = 7
$a[9] = 8
$a[10] = 8

Local $aOnlyOne = _OnlyOne($a)
_ArrayDisplay($aOnlyOne)
$aOnlyOne = _OnlyOne($a, 1)
_ArrayDisplay($aOnlyOne)

#cs
	При успехе вернет одномерный массив с элементами проверяемого одномерного массива, которые входят в проверяемый массив только один раз.
	$i_CountInNul	- 0 - не возвращать количество найденных элементов в 0-индексе (по умолчанию), 1 - возвращать.
	$i_Start		- стартовый индекс проверяемого массива, может быть только 0 (по умолчанию) или 1.
	При неудаче флаг @error:
	1	- массив $a_Array не одномерный.
	2	- ошибка создания объекта Scripting.Dictionary
#ce
Func _OnlyOne($a_Array, $i_CountInNul = 0, $i_Start = 0)
	If UBound($a_Array, 0) <> 1 Then Return SetError(1)
	Switch $i_Start
		Case 0, 1
		Case Else
			$i_Start = 0
	EndSwitch
	Switch $i_CountInNul
		Case 0, 1
		Case Else
			$i_CountInNul = 0
	EndSwitch
	Local $o_Dict = ObjCreate('Scripting.Dictionary')
	If @error Then Return SetError(2)
	For $i = $i_Start To UBound($a_Array) - 1
		$o_Dict.Item($a_Array[$i]) = $o_Dict.Item($a_Array[$i]) + 1
	Next
	Local $i_Count = $o_Dict.Count, $a_Ret[$i_Count + $i_CountInNul], $a_Keys = $o_Dict.Keys, $i_Ind = 0
	For $i = 0 To $i_Count - 1
		If $o_Dict.Item($a_Keys[$i]) = 1 Then
			$a_Ret[$i_Ind + $i_CountInNul] = $a_Keys[$i]
			$i_Ind += 1
		EndIf
	Next
	If $i_Ind <> $i_Count Then ReDim $a_Ret[$i_Ind + $i_CountInNul]
	If $i_CountInNul Then $a_Ret[0] = $i_Ind
	Return $a_Ret
EndFunc   ;==>_OnlyOne
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
C2H5OH [?]
imho, правильный путь здесь
Вариант с Assign - Eval по скорости примерно одинаков с вариантом с Scripting.Dictionary, но при больших объемах проверки памяти жрет очень много. Из-за этого для себя я его перестал использовать.
 

Garrett

Модератор
Локальный модератор
Сообщения
3,999
Репутация
967
Код:
#include<Array.au3>
Local $a[11]
$a[0] = 1
$a[1] = 1
$a[2] = 2
$a[3] = 2
$a[4] = 3
$a[5] = 5
$a[6] = 1
$a[7] = 6
$a[8] = 7
$a[9] = 8
$a[10] = 8

_ArrayDisplay($a, StringFormat('Dim: %.d', UBound($a)))
$t = TimerInit()
_ArrayRemoveDuplicateItems($a, 0)		
_ArrayDisplay($a, StringFormat('Dim: %d / Time: %.4f sec', UBound($a), TimerDiff($t) / 1000))


Код:
;~ 0 = vbBinaryCompare - binary comparison
;~ 1 = vbTextCompare - textual comparison
Func _ArrayRemoveDuplicateItems(ByRef $a_Array, $i_Mode = 1)
    
    Local $o_Obj, $i_UBound
    
    $o_Obj = ObjCreate("Scripting.Dictionary")
    If @error Then SetError(1, 0, 0)
    
    $o_Obj.CompareMode = $i_Mode
    
    For $i = 0 To UBound($a_Array)-1
		
        If Not $o_Obj.Exists($a_Array[$i]) Then
			$o_Obj.Add($a_Array[$i], 1)
		Else
			$o_Obj.Item($a_Array[$i]) = $o_Obj.Item($a_Array[$i]) + 1
		EndIf
    Next
	
	$aKey = $o_Obj.Keys
	$aItem = $o_Obj.Items
	
	For $i = 0 To $o_Obj.Count -1
		If $aItem[$i] > 1 Then  $o_Obj.Remove($aKey[$i])
	Next

    $a_Array = $o_Obj.Keys
    $i_UBound = $o_Obj.Count
    $o_Obj = 0
    
    Return SetError(0, $i_UBound, 1)
EndFunc; ==>_ArrayRemoveDuplicateItems
 
Автор
C

classick

Новичок
Сообщения
16
Репутация
1
Мужики, спасибо, попытался разобраться во всех вариантах выше (насколько хватило начальных знаний), но это не совсем то, что мне необходимо. Ничего из них не выводит нужные мне элементы (только те, которые не повторяются вообще)

2 Garrett,
Насколько я понял, Ваш вариант найдет дубли в массиве. Касательно ф-и _ArrayRemoveDuplicateItems - в конечном массиве все-равно выводит все элементы (в том числе и дубли)

На всякий случай ниже приведу весь код (строго не судите, т.к. новичек в написании) и файлы для сравнения:
Код:
#include <File.au3>
#include <Array.au3>

Local $FSDir = "C:\Common\WorkFiles\Scripts\"
Local $statDir = "C:\Common\Statistics\"
Local $sMask = "*.acsauto"
Local $hMask = "*.HTML"
Local $t = 1
Local $StatDoneArray[1] ;Массив для готовой статистики
$statList = _FileListToArray($statDir, $hMask) ;Получаем список созданной статистики в массив statList

Do
   $statLine = StringSplit( $statList[$t], '.') ;делим строку по аргументу '.'
   _ArrayAdd($StatDoneArray, $statLine[1])
   $t = $t + 1
Until $t > $statList[0]
_ArrayInsert($StatDoneArray,0,$statList[0])
_ArrayDelete($StatDoneArray,1)

Local $r = 1
Local $ScriptListArray[1] ;Массив для готовых скриптов
$scriptList = _FileListToArray($FSDir, $sMask) ;Получаем список созданных скриптов в массив scriptList
Do
   $scriptLine = StringSplit( $scriptList[$r], '.') ;делим строку по аргументу '.'
   _ArrayAdd($ScriptListArray, $scriptLine[1])
   $r = $r + 1
Until $r > $scriptList[0]

_ArrayInsert($ScriptListArray,0,$scriptList[0])
_ArrayDelete($ScriptListArray,1)
_ArrayDelete($StatDoneArray,0)
_ArrayDelete($ScriptListArray,0)
_ArrayConcatenate($StatDoneArray,$ScriptListArray)
_ArraySort($StatDoneArray)
$a = _ArrayUnique($StatDoneArray,0)

В двух разных директориях (*.html в $statDir, ну а *.acsauto в $FSDir) у меня лежат файлы *.html и *.acsauto:
87001.acsauto
87002.acsauto
87003.acsauto
87004.acsauto
87005.acsauto

и

87001.HTML
87002.HTML
87003.HTML
87004.HTML

Как видно под спойлером html файлов меньше. Моей задачей является получение *.acsauto файла под тем номером, которого нет в *.html.

Надеюсь так я смог лучше выразить свою проблему.

Спасибо.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
classick,
У Вас изначально не правильно была поставлена задача. И моя функция _OnlyOne(), и Garrett_ArrayRemoveDuplicateItems() (они практически одинаковые), делают то, что указано в первом посте, то есть возвращают из одномерного массива только те элементы, которые входят в него только один раз.
Надо было сразу писать про папки и файлы.
Попробуйте сделать примерно так (пути к папкам в массиве $a_Dirs поменяйте на свои). Скрипт вернет массив с файлами, имена которых (без расширения) есть только в одной из этих папок.
Код:
#include <Array.au3>

Local $a_Dirs[2][2] = [[@ScriptDir & '\Scripts\', '*.acsauto'],[@ScriptDir & '\Statistics\', '*.html']], $a_Find[2], _
		$i_Max = 100, $a_Res[$i_Max + 1] = [0], $i_Len

For $i = 0 To 1
	If Not FileExists($a_Dirs[$i][0] & $a_Dirs[$i][1]) Then Exit ConsoleWrite('В папке "' & $a_Dirs[$i][0] & '" нет файлов ' & $a_Dirs[$i][1] & @LF)
	$i_Len = StringLen($a_Dirs[$i][1]) - 1
	$a_Find[0] = FileFindFirstFile($a_Dirs[$i][0] & $a_Dirs[$i][1])
	If $a_Find[0] = -1 Then Exit ConsoleWrite('Ошибка поиска файлов ' & $a_Dirs[$i][1] & ' в папке "' & $a_Dirs[$i][0] & '"' & @LF)
	While 1
		$a_Find[1] = FileFindNextFile($a_Find[0])
		If @error Then ExitLoop
		If @extended Then ContinueLoop
		If FileExists($a_Dirs[1 - $i][0] & StringTrimRight($a_Find[1], $i_Len) & $a_Dirs[1 - $i][1]) Then ContinueLoop
		$a_Res[0] += 1
		If $a_Res[0] > $i_Max Then
			$i_Max += 100
			ReDim $a_Res[$i_Max + 1]
		EndIf
		$a_Res[$a_Res[0]] = $a_Find[1]
	WEnd
	FileClose($a_Find[0])
Next
If $a_Res[0] Then ReDim $a_Res[$a_Res[0] + 1]
_ArrayDisplay($a_Res)
 
Автор
C

classick

Новичок
Сообщения
16
Репутация
1
2 madmasles,

Спасибо! Это именно то, что мне необходимо.

Позвольте задать уточняющий вопрос:
madmasles сказал(а):
classick,
И моя функция _OnlyOne(), и Garrett_ArrayRemoveDuplicateItems() (они практически одинаковые), делают то, что указано в первом посте, то есть возвращают из одномерного массива только те элементы, которые входят в него только один раз.

В том коде, что я указал выше, я объединил 2 одномерных массива в один. Почему ф-и, которые Вы указали ранее не отрабатывают по этому одному массиву также, как Ваша последняя?

Спасибо!
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
classick [?]
В том коде, что я указал выше, я объединил 2 одномерных массива в один. Почему ф-и, которые Вы указали ранее не отрабатывают по этому одному массиву также, как Ваша последняя?
Я Ваш код особо не смотрел и не проверял, так что ответить не могу.
 
Автор
C

classick

Новичок
Сообщения
16
Репутация
1
Спасибо всем за оказанную помощь!
 

AZJIO

Меценат
Меценат
Сообщения
2,874
Репутация
1,194
classick
Посмотри функцию _Unique_Lines_Text2 в Compare strings.
Получение списка файлов через FileOperations.
Первая функция работает со списком, а не массивом, вторая - поиск файлов получает список, если установлен флаг "не массив".
 
Автор
C

classick

Новичок
Сообщения
16
Репутация
1
Пошел немножко другим способом, т.к. все вышеуказанные ну очень сложные для меня, как для новичка :smile:

Прошу оценить: :-[
Код:
_ArrayConcatenate($StatDoneArray,$ScriptListArray) 

Local $IndexToDelete[0]

For $i = UBound($StatDoneArray) - 1 To 2 Step -1
   For $j = 1 to $i-1 Step 1
	  If $StatDoneArray[$i] = $StatDoneArray[$j] Then
		 if (_ArraySearch($IndexToDelete,$i) = -1)	Then
			_ArrayAdd($IndexToDelete,$i)
			EndIf
		  if (_ArraySearch($IndexToDelete,$j) = -1)	 Then
			 _ArrayAdd($IndexToDelete,$j)
			 EndIf
	  EndIf
    Next
Next

$str=_ArrayToString($IndexToDelete,";")
 _ArrayDelete($StatDoneArray, $str )
_ArrayDisplay($StatDoneArray)
 

AZJIO

Меценат
Меценат
Сообщения
2,874
Репутация
1,194
classick
Ваш вариант намного сложнее, кроме того он ресурсоёмкий. Я предложил две функции (две строки), всё. Хотя вспомнил что нужно регулярное выражение для удаления расширений, типа (?s)\..+(?=\r\n) заменить на ничего. А после получения списка присоединить расширение. Я сейчас в Linux, пример дать не могу. В функцию передаются два списка, возвращается список отсутствующих во втором. Что именно там сложно?
 
Верх