How to swap object value by set of index in javascript - javascript

I have array like this
arrayValue=[
{ value: 1},
{ value: 2},
{ value: "line break"},
{ value: 3},
{ value: 4},
{ value: "line break"},
{ value: 1},
{ value: 1}
]
I have to swap forward or backward value by 2 set of index, each 3 index of value having up & down arrow when i am click up/down arrow have to swap based on that two index forward or backward.for example 0th, 3rd, 6th having arrow. any one help this?

If I understand you correctly, it'll be something like the below.
function _move(arr, idx, dir) {
var res = arr.slice();
var thrown = res.splice(idx, 1)[0];
if (dir === 'up') {
res.splice(idx-1, 0, thrown);
} else {
res.splice(idx+1, 0, thrown);
}
return res;
}
Array.prototype.up = function (idx) {
return _move(this, idx, 'up');
};
Array.prototype.down = function (idx) {
return _move(this, idx, 'down');
};
var a = [1, 2, 3, 4, 5];
console.log(
a.up(2), // 1 3 2 4 5
a.down(2) // 1 2 4 3 5
);
UPDATE
If you want to swap n items, left ones starting at L and right ones starting at R - call swapItems(arr, L, R, n). Here's implementation I've written:
function swapItems(arr, leftIdx, rightIdx, itemsNum) {
var leftOnes = arr.splice(leftIdx, itemsNum);
var rightOnes = arr.splice(rightIdx - itemsNum, itemsNum);
arr.splice(leftIdx, 0, ...rightOnes);
arr.splice(rightIdx, 0, ...leftOnes);
}
var arr = [1, 2, 'br', 3, 4, 'br', 1, 1];
// first pair starts at 0, another pair start at 3, number of items to affect = 2
swapItems(arr, 0, 3, 2);
// arr = [3, 4, 'br', 1, 2, 'br', 1, 1]

Related

Why is my Steinhaus-Johnson-Trotter Algorithm implementation producing duplicate permutations?

I have implemented the Steinhaus-Johnson-Trotter Algorithm using JavaScript, this implementation only calculates all permutations for arrays of numbers.
When the size of the array is >= 4 the algorithm is producing duplicate results. I see why it produces the results, but I am not sure how to avoid this, as creating these duplicates satisfies the algorithms principles.
class Direction {
constructor(dir) {
if(dir === 'LEFT' || dir === 'RIGHT') {
this.dir = dir
}
}
setDir(dir) {
if(dir === 'LEFT' || dir === 'RIGHT') {
this.dir = dir
}
}
switchDir() {
switch(this.dir) {
case 'LEFT':
this.dir = 'RIGHT'
break
case 'RIGHT':
this.dir = 'LEFT'
break
}
}
}
var permute = function(nums) {
if(nums.length === 1) return [nums]
if(nums.length === 2) return [nums, [nums[1], nums[0]]]
// I'm only worried about arrays up to length 6
const facts = [1, 2, 6, 24, 120, 720]
const dirs = {}
const max = Math.max(...nums)
nums.forEach(v => {
dirs[v] = new Direction('LEFT')
})
const res = []
const move = (n) => {
const i = nums.indexOf(n)
const ele = dirs[n]
switch(ele.dir) {
case 'LEFT':
[nums[i], nums[i - 1]] = [nums[i - 1], nums[i]]
break
case 'RIGHT':
[nums[i], nums[i + 1]] = [nums[i + 1], nums[i]]
break
}
if(n === max) {
return
}
nums.forEach(v => {
if(v > n) dirs[v].switchDir()
})
}
// Number is said to mobile if it can move to its direction
const isMobile = (n) => {
const d = dirs[n].dir
if(d === 'LEFT' && nums.indexOf(n) !== 0) {
return true
}
if(d === 'RIGHT' && nums.indexOf(n) !== nums.length - 1) {
return true
}
return false
}
// Finding mobiles means finding the largest number and checking if it is mobile
const findMobile = () => {
// If not max then lets find the next largest mobile
var num = Number.MIN_VALUE
nums.forEach(v => {
if(isMobile(v) && v > num) {
num = v
}
})
return num
}
// Loop through the max length factorial, included up to only 6 as req
while(res.length < facts[nums.length - 1]) {
const next = findMobile()
move(next)
res.push([...nums])
console.log(res)
}
return res
};
Test Cases:
Test 1:
Input: [1,2,3]
Result: [[1,3,2],[3,1,2],[3,2,1],[2,3,1],[2,1,3],[1,2,3]], Passed
Test 2:
Input: [5,4,6,2]
Result: [
[ 5, 6, 4, 2 ], [ 6, 5, 4, 2 ],
[ 5, 6, 4, 2 ], [ 5, 4, 6, 2 ],
[ 5, 4, 2, 6 ], [ 4, 5, 2, 6 ],
[ 4, 5, 6, 2 ], [ 4, 6, 5, 2 ],
[ 6, 4, 5, 2 ], [ 6, 4, 2, 5 ],
[ 4, 6, 2, 5 ], [ 4, 2, 6, 5 ],
[ 4, 2, 5, 6 ], [ 4, 2, 6, 5 ],
[ 4, 6, 2, 5 ], [ 6, 4, 2, 5 ],
[ 4, 6, 2, 5 ], [ 4, 2, 6, 5 ],
[ 4, 2, 5, 6 ], [ 4, 5, 2, 6 ],
[ 4, 5, 6, 2 ], [ 4, 6, 5, 2 ],
[ 6, 4, 5, 2 ], [ 6, 5, 4, 2 ]
], Failed
As shown in the results, the algorithm is producing duplicates, however as mentioned earlier, the steps between the duplicates satisfy the algorithm.
My Understanding of the Algorithm:
All elements start out facing right to left:
ie. <1<2<3<4
We find the next largest "mobile" number, in this case 4. Then we shift it towards its direction.
ie. <1<2<4<3
Repeat the process.
If a mobile number is moved that is less than another number, the larger number has its direction swapped.
EDIT
I have solved the problem, it involved in not checking the size of the mobile number and number to be swapped.
Compare steps with this simple Python implementation (ideone link to look at results, order is similar to wiki example ).
I don't see direction items swapping together with elements in your code
def SJTperms(a, dirs):
n = len(a)
id = -1
for i in range(n):
# can check mobility mobile largest mobile
if (0<=i+dirs[i]<n) and (a[i] > a[i+dirs[i]]) and ((id == -1) or (a[i] > a[id])):
id = i
if (id == -1): #last permutation
return False
for i in range(n):
if a[i] > a[id]:
dirs[i] = - dirs[i]
#swap elements AND their directions
a[id], a[id + dirs[id]] = a[id + dirs[id]], a[id]
t = dirs[id]
dirs[id], dirs[id + t] = dirs[id + t], dirs[id]
return True
a = [1,2,3,4]
d = [-1]*len(a)
cont = True
while cont:
print(a)
#print(d)
cont = SJTperms(a, d)

How to override specific value when object value is same as other object

https://jsfiddle.net/JungEun1997/nb3o1987/50/
I have reached the desired result but you want to see it in a simpler way.
I tried using map and filter but failed.
I want to change this obj_wrap to obj_a in a simpler way!
(start_num and end_num differ by 1)
var obj_wrap = {
'time':[{
'start_num': 10,
'end_num':11
},{
'start_num': 3,
'end_num':4
},{
'start_num': 1,
'end_num':2
},{
'start_num': 2,
'end_num':3
},{
'start_num': 6,
'end_num':7
}]
}
var obj_a = {
'time':[{
'start_num': 1,
'end_num':4
},{
'start_num': 6,
'end_num':7
},{
'start_num': 10,
'end_num':11
}]
}
I used this method.
var obj_b = {'time':[]}
$.each(obj_wrap.time,function(time_key,time_val){
$.each(obj_wrap.time,function(chk_key,chk_val){
if(time_val.start_num === chk_val.end_num){
obj_wrap.time[time_key]['start_num'] = chk_val.start_num
obj_wrap.time[chk_key] = ""
}
if(time_val.end_num === chk_val.start_num){
obj_wrap.time[time_key]['end_num'] = chk_val.end_num
obj_wrap.time[chk_key] = ""
}
});
})
$.each(obj_wrap.time,function(key,value){
if(value!==""){
obj_b.time.push(value)
}
})
obj_b.time.sort(function (a, b) {
return a.start_num < b.start_num ? -1 : a.start_num > b.start_num ? 1 : 0;
});
You can use Bucket Sort algorithm in this case:
var obj_wrap = {
'time':[{
'start_num': 10,
'end_num':11
},{
'start_num': 3,
'end_num':4
},{
'start_num': 1,
'end_num':2
},{
'start_num': 2,
'end_num':3
},{
'start_num': 6,
'end_num':7
}]
};
var time = obj_wrap.time;
var bucket = [];
time.forEach(({start_num, end_num}) => {
for (var i = start_num; i < end_num; i++) {
bucket[i] = true;
}
});
var newTime = [];
bucket.forEach((_, index) => {
if (bucket[index - 1]) {
newTime[newTime.length - 1].end_num = index + 1;
} else {
newTime.push({start_num: index, end_num: index + 1});
}
});
var obj_a = {time: newTime};
console.log(obj_a);
The complexity is O(n).
I've implemented just a simple memoized cache of your data with the Key Value pair representing start_num & end_num values (See code comments for visual).
Once the memo has been created, you can iterate across the memo in linear time, and populate a results array accordingly. For that part, i opted for reduce, since it persists a transient state that's accessible for each iteration.
On each iteration, i basically just check to see if I should continue iterating without populating the times array. Once i've detected a break in the number chain, there's some edge case checks conducted before populating the times array with the expected results.
Time & Space Complexity is O(n).
const objWrap = {
time:[{
start_num: 10,
end_num:11
}, {
start_num: 3,
end_num:4
}, {
start_num: 1,
end_num:2
}, {
start_num: 2,
end_num:3
}, {
start_num: 6,
end_num:7
}],
};
const memo = objWrap.time.reduce((acc, next) => {
if (!Reflect.has(acc, next.start_num)) {
acc[next.start_num] = next.end_num;
}
return acc;
}, {});
/*
memo is now:
{
1: 2,
2: 3,
3: 4,
6: 7,
10: 11
}
NOTE: If you store key's as numbers in a JS object, they'll be automatically sorted.
*/
const entries = Object.entries(memo);
const result = entries
.slice(1) // iterate across all entries except the first since we'll use the first entry to initialize our accumulator.
.reduce((acc, [start,end], i, arr) => {
if (Reflect.has(memo, acc.next)) { // if we found a sequence, then just continue iterating.
acc.next = end;
} else {
acc.times.push({ // if the sequence is broken, then we have a result.
start_num: Number(acc.start), // cast back to number, as it's currently a string
end_num: acc.next,
});
if (i === arr.length - 1) { // if we've reached the end of the array, then prepare the last result as well.
acc.times.push({
start_num: Number(start),
start_end: end,
});
delete acc.next;
delete acc.start;
} else { // if we haven't reached the end of the array, then prepare the next iteration's comparison.
acc.start = start;
acc.next = end;
}
}
return acc;
}, {
next: entries[0][1], // initialize accumulator with first entryies end value.
start: entries[0][0], // initialize accumulator with first entryies start value.
times: [],
});
console.log(JSON.stringify(result, null, 2))

JavaScript function that returns all permutations

I've been trying to make a function that generates all the permutations of the numbers from 0 to num and stores them in a multidimensional array. I want to store in the combinations variable something like:
[ [ 1, 2, 3 ],
[ 1, 3, 2 ],
[ 2, 1, 3 ],
[ 3, 1, 2 ],
wrongly [ 2, 3, 1 ],
[ 3, 2, 1 ] ]
but instead I get:
[ [ 3, 2, 1 ],
[ 3, 2, 1 ],
[ 3, 2, 1 ],
[ 3, 2, 1 ],
[ 3, 2, 1 ],
[ 3, 2, 1 ] ]
My function is:
var combinations = [];
function comb(num, index, list, used) {
if (num == index)
combinations.push(list);
else {
for (var i = 0; i < num; ++i) {
if (!used[i]) {
list[i] = index + 1;
used[i] = true;
comb(num, index + 1, list, used);
used[i] = false;
}
}
}
}
I usually program with C++, so I think that I am using the arrays wrongly.
Your issue is that the list array you pass into your recursive call is going to be changed as it is essentially passed by reference, so list will lose its previous combinations. Instead, you can make a copy of the array before you pass it into your recursive call using the spread syntax (...).
See working example below:
var combinations = [];
function comb(num, index, list, used) {
if (num == index)
combinations.push(list);
else {
for (var i = 0; i < num; ++i) {
if (!used[i]) {
list[i] = index + 1;
used[i] = true;
comb(num, index + 1, [...list], used);
used[i] = false;
}
}
}
}
comb(3, 0, [], []);
console.log(combinations); // [[1,2,3],[1,3,2],[2,1,3],[3,1,2],[2,3,1],[3,2,1]]
.as-console-wrapper { max-height: 100% !important; top: 0;}

How can I sort an array based on "before" and "after" conditions in JavaScript?

I want to sort an array based on "before" and "after" conditions.
Example:
C should be before B: Example: {key: 'C', condition: {$before: 'B'}
B should be after A: Example: {key: 'B', condition: {$after: 'A'}
A: Example {key: 'A'}.
The resulting sorted list would be: A, C, B.
I need this so I can sort a list of middleware in a pipeline. Each middleware has a condition and I should come up with an order in which all conditions from all middleware are met. This is similar to what MS Project does to organize tasks in a Gantt given what are the requirements for a task.
Question: What is the easiest way to achieve it? Even by using external libraries like underscore? Bonus: Does this kind of sorting have a name better than "conditional sorting"? That would help on my Google searches.
EDIT: The input should be an array of items with their conditions. I can't hardcode the sort method.
EDIT 2: As #Berdi stated. This is called typological sorting. Yes, depending on the items and conditions, there might be no combination that meet all the conditions and my algorithm should trigger an exception.
EDIT 3: The way I'm thinking of implementing this is calculating all the possible combinations and then look for the first one that meet all the conditions. This may not be terribly slow for me because, in my case, I can do this just once when the app starts and I'm not going to have more than 50 items in the array. But anyway, for the science, it would be good to know a more optimized solution.
EDIT 4: I would accept a solution that works with after conditions only. Like MS Project.
Bonus: Does this kind of sorting have a name better than "conditional sorting"?
It's called topological sort. And depending on your exact input format and content, it might not even be well-defined.
I would use: a hash table, a pointer for the start and then reassembling the array.
(This answer is part of my answer from this question: Ordering array of objects that have a BeforeID and AfterID on each object)
function chain(array) {
var o = {}, pointer;
array.forEach(function (a) {
o[a.id] = a;
if (a.beforeId === null) {
pointer = a.id;
}
});
array = [];
do {
array.push(o[pointer]);
pointer = o[pointer].afterId;
} while (pointer !== null);
return array;
}
var unsorted = [{ id: 7, beforeId: 6, afterId: 8 }, { id: 11, beforeId: 10, afterId: null }, { id: 0, beforeId: null, afterId: 1 }, { id: 1, beforeId: 0, afterId: 2 }, { id: 4, beforeId: 3, afterId: 5 }, { id: 8, beforeId: 7, afterId: 9 }, { id: 2, beforeId: 1, afterId: 3 }, { id: 9, beforeId: 8, afterId: 10 }, { id: 10, beforeId: 9, afterId: 11 }, { id: 3, beforeId: 2, afterId: 4 }, { id: 5, beforeId: 4, afterId: 6 }, { id: 6, beforeId: 5, afterId: 7 }];
document.write('<pre>' + JSON.stringify(chain(unsorted), 0, 4) + '</pre>');
A Solution for EDIT 4, after conditions only.
function chain(array) {
var n = {}, o = {}, pointer;
array.forEach(function (a) {
o[a.id] = a;
n[a.after] = a.id;
if (a.after === null) {
pointer = a.id;
}
});
// rewind pointer.
// i took push for the array. otherwise the array could be mounted
// from the other end with unshift but push is more efficient.
do {
pointer = n[pointer];
} while (pointer in n);
array = [];
do {
array.push(o[pointer]);
pointer = o[pointer].after;
} while (pointer !== null);
return array;
}
var unsorted = [{ id: 7, after: 8 }, { id: 11, after: null }, { id: 0, after: 1 }, { id: 1, after: 2 }, { id: 4, after: 5 }, { id: 8, after: 9 }, { id: 2, after: 3 }, { id: 9, after: 10 }, { id: 10, after: 11 }, { id: 3, after: 4 }, { id: 5, after: 6 }, { id: 6, after: 7 }, ];
document.write('<pre>' + JSON.stringify(chain(unsorted), 0, 4) + '</pre>');
Edit - with sparse before/after
function chain(array) {
var after = {}, before = {}, o = {}, pointer = array[0].id;
array.forEach(function (a) {
o[a.id] = a;
if (a.after !== null) {
after[a.id] = a.after;
before[a.after] = a.id;
}
if (a.before !== null) {
before[a.id] = a.before;
after[a.before] = a.id;
}
});
document.write('<pre>before ' + JSON.stringify(before, 0, 4) + '</pre>');
document.write('<pre>after ' + JSON.stringify(after, 0, 4) + '</pre>');
do {
document.write('pointer: ' + pointer + '<br>');
pointer = before[pointer];
} while (pointer in before);
document.write('pointer: ' + pointer + '<br>');
array = [];
do {
array.push(o[pointer]);
pointer = after[pointer];
} while (pointer !== undefined);
return array;
}
var unsorted = [{ id: 'C', before: 'B', after: null }, { id: 'B', before: null, after: null }, { id: 'A', before: null, after: 'B' }];
document.write('<pre>' + JSON.stringify(chain(unsorted), 0, 4) + '</pre>');
This may be of use.
function sortByRules(rules, arr) {
var rulesEvaluated = [];
rules.forEach(rule => {
applyRule(rule, arr);
rulesEvaluated.push(rule);
if (conflicts(rulesEvaluated, arr)){
throw new Error('rule conflict');
}
});
return arr;
}
function conflicts(rules, arr) {
return rules.some(r => r.condition.before ?
arr.indexOf(r.key)>arr.indexOf(r.condition.before) :
arr.indexOf(r.key)<arr.indexOf(r.condition.after));
}
function applyRule(rule, arr) {
var increment = rule.condition.before ? -1 : 1;
var item = arr.splice(arr.indexOf(rule.key), 1)[0];
var target = rule.condition.before || rule.condition.after;
arr.splice(Math.max(0, arr.indexOf(target) + increment), 0, item);
}
var toSort = ['b', 'd', 'c', 'a'];
var rules = [ { key: 'b', condition: { before: 'c' } },
{ key: 'd', condition: { after: 'c' } },
{ key: 'a', condition: { before: 'b' } } ];
document.write(sortByRules(rules, toSort)); // ['a', 'b', 'c', 'd']
Create an object storing the order you need, then use [].sort() as usual:
var sorter = {
'A': 1,
'B': 3,
'C': 2
}
var input = ['A', 'B', 'C'];
input = input.sort(function sort(a, b) {
return sorter[a] > sorter[b];
});
console.log(input);
document.write("<pre>" + input + "</pre>");
Now, you can sort any array containing any combination of A, B and C.
A, B, C can be anything, a property, its length, the first letter of a value...

Javascript array contains/includes sub array

I need to check if an array contains another array. The order of the subarray is important but the actual offset it not important. It looks something like this:
var master = [12, 44, 22, 66, 222, 777, 22, 22, 22, 6, 77, 3];
var sub = [777, 22, 22];
So I want to know if master contains sub something like:
if(master.arrayContains(sub) > -1){
//Do awesome stuff
}
So how can this be done in an elegant/efficient way?
With a little help from fromIndex parameter
This solution features a closure over the index for starting the position for searching the element if the array. If the element of the sub array is found, the search for the next element starts with an incremented index.
function hasSubArray(master, sub) {
return sub.every((i => v => i = master.indexOf(v, i) + 1)(0));
}
var array = [12, 44, 22, 66, 222, 777, 22, 22, 22, 6, 77, 3];
console.log(hasSubArray(array, [777, 22, 22]));
console.log(hasSubArray(array, [777, 22, 3]));
console.log(hasSubArray(array, [777, 777, 777]));
console.log(hasSubArray(array, [42]));
var master = [12, 44, 22, 66, 222, 777, 22, 22, 22, 6, 77, 3];
var sub = [777, 22, 22];
console.log(master.join(',').includes(sub.join(',')))
//true
You can do this by simple console.log(master.join(',').includes(sub.join(','))) this line of code using include method
The simplest way to match subset/sub-array
const master = [12, 44, 22, 66, 222, 777, 22, 22, 22, 6, 77, 3];
const sub1 = [777, 44, 222];
const sub2 = [777, 18, 66];
sub1.every(el => master.includes(el)); // reture true
sub2.every(el => master.includes(el)); // return false
Just came up with quick thought , but efficiency depends on size of the array
var master = [12, 44, 22, 66, 222, 777, 22, 22, 22, 6, 77, 3];
var sub = [777, 22, 22];
if ((master.toString()).indexOf(sub.toString()) > -1 ){
//body here
}
It’s surprising how often this is implemented incorrectly.
What we’re looking for is a substring in the mathematical sense.
In mathematics, a sequence is an enumerated collection of objects in which repetitions are allowed and order matters.
In mathematics, a subsequence of a given sequence is a sequence that can be derived from the given sequence by deleting some or no elements without changing the order of the remaining elements.
A subsequence which consists of a consecutive run of elements from the original sequence, such as ⟨ B, C, D ⟩ from ⟨ A, B, C, D, E, F ⟩ is a substring.
Note that a “string”, here, can consist of any element and is not limited to Unicode code-point sequences.
Effectively all previous answers have one of many possible flaws:
The string concatenation approach (array1.toString().includes(array2.toString())) fails when your array elements have commas. (Example: [ "a", "b" ] does not contain [ "a,b" ]).
Some implementations check beyond array bounds. (Example: [ "3" ] does not contain [ "3", undefined ], just because array[1] reports undefined for both).
Some implementations fail to handle repetition correctly.
Some implementations aren’t checking for substrings (in the mathematical sense) correctly, but for subsets or subsequences or something else.
Some implementations don’t account for the empty array. The empty string is the substring of every string.
Check if an array constitutes a “substring” of another array
Right off the bat, this handles the empty array correctly.
Then, it builds a list of candidate starting indexes by matching against the first element of the potential subarray.
Find the first candidate where every element of the slice matches index by index with the full array, offset by the candidate starting index.
The checked index also has to exist within the full array, hence Object.hasOwn.
const isSubArray = (full, slice) => {
if(slice.length === 0){
return true;
}
const candidateIndexes = full
.map((element, fullIndex) => ({
matched: element === slice[0],
fullIndex
}))
.filter(({ matched }) => matched),
found = candidateIndexes
.find(({ fullIndex }) => slice.every((element, sliceIndex) => Object.hasOwn(full, fullIndex + sliceIndex) && element === full[fullIndex + sliceIndex]));
return Boolean(found);
};
console.log(isSubArray([], []) === true);
console.log(isSubArray([ 0 ], []) === true);
console.log(isSubArray([ 0, 1, 2 ], [ 1, 2 ]) === true);
console.log(isSubArray([ 0, 1, 1, 2 ], [ 0, 1, 2 ]) === false);
console.log(isSubArray([ 2, 1 ], [ 1, 2 ]) === false);
console.log(isSubArray([ 1, 2, 3 ], [ 2, 3, undefined ]) === false);
console.log(isSubArray([ 0, 1, 1, 2, 3 ], [ 1, 1, 2 ]) === true);
console.log(isSubArray([ 0, 1, 1, 2, 3 ], [ 1, 2 ]) === true);
console.log(isSubArray([ 0, 1, 1, 2, 3 ], [ 0, 1, 1, 1 ]) === false);
console.log(isSubArray([ "a", "b" ], [ "a,b" ]) === false);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This has quadratic complexity, yes.
There might be more efficient implementations using Trees or Ropes.
You might also want to research some efficient substring search algorithms and try to apply them to this problem.
Get the index of the found “substring”, or -1 if not found
It’s basically the same code, but with return true; replaced by return 0;, and return Boolean(found); replaced by return found?.fullIndex ?? -1;.
const findSubArrayIndex = (full, slice) => {
if(slice.length === 0){
return 0;
}
const candidateIndexes = full
.map((element, fullIndex) => ({
matched: element === slice[0],
fullIndex
}))
.filter(({ matched }) => matched),
found = candidateIndexes
.find(({ fullIndex }) => slice.every((element, sliceIndex) => Object.hasOwn(full, fullIndex + sliceIndex) && element === full[fullIndex + sliceIndex]));
return found?.fullIndex ?? -1;
};
console.log(findSubArrayIndex([], []) === 0);
console.log(findSubArrayIndex([ 0 ], []) === 0);
console.log(findSubArrayIndex([ 0, 1, 2 ], [ 1, 2 ]) === 1);
console.log(findSubArrayIndex([ 0, 1, 1, 2 ], [ 0, 1, 2 ]) === -1);
console.log(findSubArrayIndex([ 2, 1 ], [ 1, 2 ]) === -1);
console.log(findSubArrayIndex([ 1, 2, 3 ], [ 2, 3, undefined ]) === -1);
console.log(findSubArrayIndex([ 0, 1, 1, 2, 3 ], [ 1, 1, 2 ]) === 1);
console.log(findSubArrayIndex([ 0, 1, 1, 2, 3 ], [ 1, 2 ]) === 2);
console.log(findSubArrayIndex([ 0, 1, 1, 2, 3 ], [ 0, 1, 1, 1 ]) === -1);
console.log(findSubArrayIndex([ "a", "b" ], [ "a,b" ]) === -1);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Semi-acceptable alternative: JSON
JSON-encoding both arrays might be a viable strategy as well.
Here, the surrounding […] of the potential subarray need to be removed, then an includes will tell you if the JSON string is included in the other JSON string.
This works — as opposed to the simple string concatenation or join approach — because JSON has delimiters that cannot appear verbatim in the encoded elements; if they do appear in the original elements, they’d be correctly escaped.
The caveat is that this won’t work for values that are not encodable in JSON.
const isSubArray = (full, slice) => JSON.stringify(full)
.includes(JSON.stringify(slice).replaceAll(/^\[|\]$/g, ""));
console.log(isSubArray([], []) === true);
console.log(isSubArray([ 0 ], []) === true);
console.log(isSubArray([ 0, 1, 2 ], [ 1, 2 ]) === true);
console.log(isSubArray([ 0, 1, 1, 2 ], [ 0, 1, 2 ]) === false);
console.log(isSubArray([ 2, 1 ], [ 1, 2 ]) === false);
console.log(isSubArray([ 1, 2, 3 ], [ 2, 3, undefined ]) === false);
console.log(isSubArray([ 0, 1, 1, 2, 3 ], [ 1, 1, 2 ]) === true);
console.log(isSubArray([ 0, 1, 1, 2, 3 ], [ 1, 2 ]) === true);
console.log(isSubArray([ 0, 1, 1, 2, 3 ], [ 0, 1, 1, 1 ]) === false);
console.log(isSubArray([ "a", "b" ], [ "a,b" ]) === false);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If the order is important, it has to be an actually sub-array (and not the subset of array) and if the values are strictly integers then try this
console.log ( master.join(",").indexOf( subarray.join( "," ) ) == -1 )
for checking only values check this fiddle (uses no third party libraries)
var master = [12, 44, 22, 66, 222, 777, 22, 22, 22, 6, 77, 3];
var sub = [777, 22, 22];
function isSubset( arr1, arr2 )
{
for (var i=0; i<arr2.length; i++)
{
if ( arr1.indexOf( arr2[i] ) == -1 )
{
return false;
}
}
return true;
}
console.log( isSubset( master, sub ) );
There are faster options explained here as well.
EDIT
Misunderstood question initially.
function arrayContainsSub(arr, sub) {
var first = sub[0],
i = 0,
starts = [];
while (arr.indexOf(first, i) >= 0) {
starts.push(arr.indexOf(first, i));
i = arr.indexOf(first, i) + 1;
}
return !!starts
.map(function(start) {
for (var i = start, j = 0; j < sub.length; i++, j++) {
if (arr[i] !== sub[j]) {
return false;
}
if (j === sub.length - 1 && arr[i] === sub[j]) {
return true;
}
};
}).filter(function(res) {
return res;
}).length;
}
This solution will recursively check all available start points, so points where the first index of the sub has a match in the array
Old Answer Kept in case useful for someone searching.
if(master.indexOf(sub) > -1){
//Do awesome stuff
}
Important to remember that this will only match of master literally references sub. If it just contains an array with the same contents, but references a different specific object, it will not match.
You can try with filter and indexOf like this:
Note: This code works in case we do not cover the order in sub array.
Array.prototype.arrayContains = function (sub) {
var self = this;
var result = sub.filter(function(item) {
return self.indexOf(item) > -1;
});
return sub.length === result.length;
}
Example here.
UPDATED: Return index of sub array inside master (cover order in sub array)
Array.prototype.arrayContains = function(sub) {
var first;
var prev;
for (var i = 0; i < sub.length; i++) {
var current = this.indexOf(sub[i]);
if (current > -1) {
if (i === 0) {
first = prev = current;
continue;
} else {
if (++prev === current) {
continue;
} else {
return -1;
}
}
} else {
return -1;
}
}
return first;
}
Demo: here
For this answer, I am preserving the order of sub-array. Means, the elements of sub-array should be in Consecutive order. If there is any extra element while comparing with the master, it will be false.
I am doing it in 3 steps:
Find the index of the first element of sub in the master and store it an array matched_index[].
for each entry in matched_index[] check if each element of sub is same as master starting from the s_index. If it doesn't match then return false and break the for loop of sub and start next for-loop for next element in matched_index[]
At any point, if the same sub array is found in master, the loop will break and return true.
function hasSubArray(master,sub){
//collect all master indexes matching first element of sub-array
let matched_index = []
let start_index = master.indexOf(master.find(e=>e==sub[0]))
while(master.indexOf(sub[0], start_index)>0){
matched_index.push(start_index)
let index = master.indexOf(sub[0], start_index)
start_index = index+1
}
let has_array //flag
for(let [i,s_index] of matched_index.entries()){
for(let [j,element] of sub.entries()){
if(element != master[j+s_index]) {
has_array = false
break
}else has_array = true
}
if (has_array) break
}
return has_array
}
var master = [12, 44, 22, 66, 222, 777, 22, 22, 22, 6, 77, 3];
console.log(hasSubArray(master, [777, 22, 22]));
console.log(hasSubArray(master, [777, 22, 3]));
console.log(hasSubArray(master, [777, 777, 777]));
console.log(hasSubArray(master, [44]));
console.log(hasSubArray(master, [22, 66]));
I had a similar problem and resolved it using sets.
function _hasSubArray( mainArray, subArray )
{
mainArray = new Set( mainArray );
subArray = new Set( subArray );
for ( var element of subArray )
{
if ( !mainArray.has( element ) )
{
return false;
}
}
return true;
}
If run this snippet below it should work
x = [34, 2, 4];
y = [2, 4];
y.reduce((included, num) => included && x.includes(num), true);
EDIT:
#AlexanderGromnitsky You are right this code is incorrect and thank you for the catch! The above code doesn't actually do what the op asked for. I didn't read the question close enough and this code ignores order. One year later here is what I came up with and hopefully this may help someone.
var master = [12, 44, 22, 66, 222, 777, 22, 22, 22, 6, 77, 3];
var sub = [777, 22, 22];
var is_ordered_subset = master.join('|').includes(sub.join('|'))
This code is somewhat elegant and does what op asks for. The separator doesn't matter as long as its not an int.
async function findSelector(a: Uint8Array, selector: number[]): Promise<number> {
let i = 0;
let j = 0;
while (i < a.length) {
if (a[i] === selector[j]) {
j++;
if (j === selector.length) {
return i - j + 1;
}
} else {
j = 0;
}
i++;
}
return -1;
}
Try using every and indexOf
var mainArr = [1, 2, 3, 4, 5]
var subArr = [1, 2, 3]
function isSubArray(main, sub) {
return sub.every((eachEle) => {
return (main.indexOf(eachEle) + 1);
});
}
isSubArray(mainArr, subArr);

Categories