0

For example,

const target = [1,2,3,4,5,"abc", "def", [10,1000]]

test(target, [2,3,4]) === true
test(target, [4,5,"abc"]) === true
test(target, ["def", [10, 1000]) === true
test(target, [0,1,2]) === false
test(target, ["abc",5,4]) === false
test(target, [2,4]) === false
test(target, ["def", [1000, 10]]) === false

Currently I'm using JSON.stringify to accomplish this (and with replacer function if the elements contain things like BigInt values):

`,${JSON.stringify(target).slice(1, -1)},`.includes(`,${JSON.stringify(arr).slice(1, -1)},`)

to check if target contains arr with the same consecutive ordering of elements, however this seems a bit too hacky so I wonder if there are better ways to accomplish it?

EDIT:

I'm not sure why this question is marked as a duplicate of Check if an array includes an array in javascript since it's a completely different question?

EDIT 2:

I have edited the title to be more precise, to match what I really meant and what the examples here have shown. And I still don't think it's a duplicate of Javascript array contains/includes sub array since "sub array" doesn't sound like the same thing and it says The order of the subarray is important but the actual offset it not important which again not sure if it's about the same question.

Also I'm aware that this can be done by brute force loop/recursion kind of solutions instead of the JSON.stringify "hack" I use, I'm more interested in whether there are more performant, or safer, or more idiomatic ways to do it than this "hackish" solution (And I know JSON.stringify cannot serialize things like BigInt, which needs a replacer function to make it work)

EDIT 3:

Add more examples to illustrate what I try to accomplish better.

7
  • 2
    Does this answer your question? Javascript array contains/includes sub array Commented Oct 15, 2022 at 14:22
  • @Raymond Chen, not sure the "sub array" thing is the same meaning here, I want the exact same ordering of elements, that test(target, [2,4]) === false Commented Oct 16, 2022 at 5:04
  • @hellopeach Strange, seems like that was the wrong target. This one should be correct. I have posted an answer that addresses all other answers’ flaws. Please note that [ 2, 4 ] is the same ordering of elements. What you’re after is a consecutive run of elements. Commented Oct 16, 2022 at 6:28
  • @Sebastian Simon, again not sure the "sub array" thing is the same meaning here, and your answer was not even there when I posted this question, it was not the accepted answer, nor was it the most voted answer, so I don't think it's a good reason to mark this question as a duplicate of that. Commented Oct 16, 2022 at 9:31
  • @hellopeach To me, it does look like the same problem. The fact that I just wrote the answer doesn’t actually matter for the duplicate system. You’ve added arrays as elements of the target array; this is actually a separate problem: the equality semantics. There are different equality semantics, e.g. SameValue, SameValueZero, etc. I chose IsStrictlyEqual (i.e. ===). Object comparison is a different semantic and can be implemented with one of the examples at How to determine equality for two JavaScript objects?. Commented Oct 16, 2022 at 11:17

3 Answers 3

2

To check for an ordered sub-array, including rejecting non-consecutive elements, we can use recursion: Array (A) is ordered inside another array (B) if first element of A is found, and if the rest of A, is inside the rest of B immediately after the found element.

function test(b, a, bIndex) {
  if (!a.length) return true;
  bIndex ??= b.findIndex(el => el === a[0]);
  return a[0] !== b[bIndex] ? false : test(b.slice(bIndex+1), a.slice(1), 0);
}

const target = [1,2,3,4,5,"abc", "def"]

console.log(test(target, [2,3,4]) === true)
console.log(test(target, [4,5,"abc"]) === true)
console.log(test(target, [0,1,2]) === false)
console.log(test(target, ["abc",5,4]) === false)
console.log(test(target, [2,4]) === false)

The original version, that isn't sensitive to non-consecutive elements, relaxing the immediately after constraint.

function isOrderedSubArray(b, a) {
  if (!a.length) return true;
  let index = b.findIndex(el => el === a[0]);
  return index === -1 ? false : isOrderedSubArray(b.slice(index + 1), a.slice(1));
}

// testing it...

let bigArray = [0, 1, 2, 3, 4, 5, 6];
let smallA = [2, 5, 6];
let smallB = [2, 5, 12];
let smallC = [4, 3, 5];
let smallD = [6, undefined];

console.log(isOrderedSubArray(bigArray, smallA)===true)
console.log(isOrderedSubArray(bigArray, bigArray)===true)
console.log(isOrderedSubArray(bigArray, smallB)===false)
console.log(isOrderedSubArray(bigArray, smallC)===false)
console.log(isOrderedSubArray(bigArray, smallD)===false)

Sign up to request clarification or add additional context in comments.

6 Comments

Smart catch, @SebastianSimon. The entry condition was too lax. Only [] is a subset of []. Edited
I just noticed, this fails the test(target, [2, 4]) test. index is just the first occurrence within the big array of the next element within the small array, which doesn’t guarantee that all elements must be consecutive.
@SebastianSimon, What result do you expect for test(target, [2, 4]) ?
The same as mentioned in the question: false.
I see it now. I read "contains with the same ordering", literally, and I missed that the OP expects false. I'll make an edit to offer an alternative. (an alternative in case the OP mis-stated their expectation).
|
1

You could get the start index and check every item of the second array.

const
    test = (a, b) => {
        let offset = a.indexOf(b[0]);
        while (offset !== -1) {
            if (b.every((v, i) => (i + offset) in a && v === a[i + offset])) return true;
            offset = a.indexOf(b[0], offset + 1);
        }
        return false;
    },
    target = [1, 2, 3, 4, 5, "abc", "def"];

console.log(test(target, [2, 3, 4]));                  //  true
console.log(test(target, [4, 5, "abc"]));              //  true
console.log(test(target, [0, 1, 2]));                  // false
console.log(test(target, ["abc", 5, 4]));              // false
console.log(test(target, [2, 4]));                     // false
console.log(test(target, []));                         // false
console.log(test(target, [ "abc", "def", undefined])); // false
.as-console-wrapper { max-height: 100% !important; top: 0; }

6 Comments

Shouldn’t test(target, []) be true? The empty subarray is always contained in every array, just like the empty substring is always contained in every string.
@SebastianSimon, for me, it should be false. but op may want a differnt result.
I think math sets is the right way to think of it. [] is a subset of every set, and its only subset is [].
@danh, sets have no order.
@NinaScholz Then it’s not a set, but a sequence. The empty sequence is a subsequence of every sequence; more specifically for this problem, the same applies to substrings. Subsequences are n-tuples, which can be generalized to sets.
|
1

This could be tidied up but I think it will be quite performant.

  1. If the first element of the sub array does not appear in the target return false
  2. iterate over the target for the length of sub, from the starting position x
  3. If the position of the sub element is not equal to its relative position in the target element, or we go beyond the length of the target array, return false
  4. If all these checks pass return true

const target = [1,2,3,4,5,"abc", "def"]

function test(arr, sub) {
    x = arr.indexOf(sub[0])
    i = 0
    while(i < sub.length ) { 
    if(i != sub.indexOf(arr[x],i) || x >= arr.length) return false;
        x+=1
        i+=1
    }
    return true;
}

console.log(test(target, [2,3,4]))// === true
console.log(test(target, [4,5,"abc"]))// === true
console.log(test(target, [0,1,2]))// === false
console.log(test(target, ["abc",5,4]))// === false
console.log(test(target, [2,4]))// === false
console.log(test(target, [])) // === true
console.log(test(target, [ "abc", "def", undefined ])) // === false
console.log(test([ 4, 5, 5, 6 ], [ 5, 5 ])) /// === true

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.