5
\$\begingroup\$

Having an array of objects, such as numbers, what would be the most optimal (Memory and CPU efficiency) way if finding a sub group of objects?

As an example:

demoArray = [1,2,3,4,5,6,7]

Finding [3,4,5] would return 2, while looking for 60 would return -1. The function must allow for wrapping, so finding [6,7,1,2] would return 5

I have a current working solution, but I'd like to know if it could be optimized in any way.

var arr = [
 1,
 5,2,6,8,2,
 3,4,3,10,9,
 1,5,7,10,3,
 5,6,2,3,8,
 9,1]
var idx = -1
var group = []
var groupSize = 0
function findIndexOfGroup(g){
 group = g
 groupSize = g.length
 var beginIndex = -2
 
 while(beginIndex === -2){
 beginIndex = get()
 }
 
 return beginIndex
}
function get(){
 idx = arr.indexOf(group[0], idx+1);
 
 if(idx === -1 || groupSize === 1){
 return idx;
 }
 var prevIdx = idx
 
 for(var i = 1; i < groupSize; i++){
 idx++
 
 if(arr[getIdx(idx)] !== group[i]){
 idx = prevIdx
 break
 }
 
 if(i === groupSize - 1){
 return idx - groupSize + 1
 }
 }
 return -2
}
function getIdx(idx){
 if(idx >= arr.length){
 return idx - arr.length
 }
 return idx
}
console.log(findIndexOfGroup([4,3,10])) // Normal
console.log(findIndexOfGroup([9,1,1,5])) // Wrapping

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Feb 8, 2017 at 14:53
\$\endgroup\$
2
  • \$\begingroup\$ Welcome to Code Review. If this is a problem from a site, it's good to have the description in the question, but having a link to the actual problem/challenge can be quite handy. I hope you have good reviews! \$\endgroup\$ Commented Feb 8, 2017 at 15:18
  • 1
    \$\begingroup\$ @Marc-Andre thanks for the welcome! This is actually a personal problem I had, just looked for the most optimal solution :) \$\endgroup\$ Commented Feb 9, 2017 at 6:33

4 Answers 4

6
\$\begingroup\$

You could first get the index of the first element of the search array.

Then loop while index !== -1 and check all elements in the search array with the elements in the base array. For checking outside of the range of the array use the reminder operator %.

Return index if all elements are equal.

If not get a new index with indexOf and an incremented start value.

Array#every breaks the iteration if the callback returns false.

function find(search, array) {
 var index = array.indexOf(search[0]);
 while (index !== -1) {
 if (search.every(function (a, i) { return a === array[(index + i) % array.length]; })) {
 return index;
 }
 index = array.indexOf(search[0], index + 1);
 }
 return -1;
}
console.log(find([3, 4, 5], [1, 2, 3, 4, 5, 6, 7])); // 2
console.log(find([6, 7, 1, 2], [1, 2, 3, 4, 5, 6, 7])); // 5
console.log(find([60], [1, 2, 3, 4, 5, 6, 7])); // -1
console.log(find([3, 4, 5], [1, 2, 3, 4, 6, 7, 3, 4, 5, 9])); // 6
.as-console-wrapper { max-height: 100% !important; top: 0; }

answered Feb 8, 2017 at 15:08
\$\endgroup\$
2
\$\begingroup\$

My take on the problem is to use slice() and compare each subarray of length equal to the group's length to the actual group array. Might take a bit long, but the code is short enough:

// The array to run the tests on
var arr = [
 1,
 5, 2, 6, 8, 2,
 3, 4, 3, 10, 9,
 1, 5, 7, 10, 3,
 5, 6, 2, 3, 8,
 9, 1
];
// Check arrays for equality, provided that both arrays are of the same length
function arraysEqual(array1, array2) {
 for (var i = array1.length; i--;) {
 if (array1[i] !== array2[i])
 return false;
 }
 return true;
}
// Returns the first index of a subarray matching the given group of objects
function findIndexOfGroup(array, group) {
 // Get the length of both arrays
 var arrayLength = array.length;
 var groupLength = group.length;
 // Extend array to check for wrapping
 array = array.concat(array);
 var i = 0;
 // Loop, slice, test, return if found
 while (i < arrayLength) {
 if (arraysEqual(array.slice(i, i + groupLength), group))
 return i;
 i++;
 }
 // No index found
 return -1;
}
// Tests
console.log(findIndexOfGroup(arr,[4,3,10])); // Normal
console.log(findIndexOfGroup(arr,[9,1,1,5])); // Wrapping
console.log(findIndexOfGroup(arr,[9,2,1,5])); // Not found

If the group is longer than the array, some errors might occur, but I leave it up to you to extend the method to deal with such situations.

answered Feb 8, 2017 at 15:20
\$\endgroup\$
1
\$\begingroup\$

i would also suggest thinking about using a library like underscore.js that has already implemented such methods or at least understand their implementation via their source code http://underscorejs.org/#contains

answered Feb 8, 2017 at 19:01
\$\endgroup\$
0
\$\begingroup\$

I think you should consider some of the edge conditions of input in addition to just reviewing your logic.

For example, you might consider validating both inputs as arrays. You can also bail out of the function early if the "needle" is empty or its size is greater than the "haystack" array size.

I also think you can pre-prepare your haystack array to deal with the looping back to the beginning of the array.

Finally, you can leverage the Array.findIndex() method as the means to iterate the haystack the seek out the first index that meets your match criteria. This basically means you can encapsulate your comparison logic in the callback to this function.

Putting it together might yield something like:

findSubArrayIndex(needle, haystack) {
 // check for valid input types
 if (!Array.isArray(needle) || (!Array.isArray(haystack)) {
 throw new TypeError('Feed me arrays!');
 }
 var needleLen = needle.length;
 var haystackLen = haystack.length;
 // if needle is empty or is longer than haystack, bail out early
 if (needleLen === 0 || needleLen > haystackLen) return -1; 
 // modify haystack to allow for loop condition
 var searchStack = haystack.concat( haystack.slice(0, needleLen - 1) );
 // search for indexMatch
 return haystack.findIndex(
 (val, idx) => {
 var needleIdx = 0
 while (needleIdx < needle.length) { 
 if (searchStack[idx] !== needle[needleIdx]) return false;
 needleIdx++;
 idx++;
 }
 return true;
 }
 );
}
answered Feb 8, 2017 at 20:55
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.