Get root property of an object using recursion - javascript

My algorithm works correctly when I apply fake objects to the function but on CodeWars it continues to fail. I'm very curious on what checks I'm missing in my code. I believe I have to use certain regular expressions but I'm very confused. Here's a link to the problem https://www.codewars.com/kata/get-root-property-name
function getRootProperty(object, val) {
var valueFound = false;
let output = '';
for (var first in object) {
var seachObject = object[first]
function query(object, val, rootName) {
Object.getOwnPropertyNames(object).forEach((value) => {
if (object[value] == val) {
valueFound = true;
output = rootName
return
} else {
query(object[value], val, rootName)
}
})
}
query(seachObject, val, first);
}
if (valueFound == false) {
return null
} else {
return output;
}
}

Luckily i only had to search new CodeWars problems and found it relatively fast: Problem description. Here is your issue: [...] other root properties may also have 9 buried in it but you should always return the first
Use if (!valueFound) query(object[value], val, rootName) for the first call (doesn't matter for the recursive calls).
The problem assumes an order in javascript properties which works in ecmascript8, partly in ecmascript6 and not at all in lower versions.

You may also do as follows;
function findRootProperty(o,x,p = this){
return Object.keys(o)
.reduce((r,k) => Array.isArray(o[k]) ? o[k].includes(x) ? r.concat(k)
: r
: r.concat(findRootProperty(o[k],x,k)),[])
.map(q => p === this ? q : p );
}
object = {
"r1n": {
"mkg": {
"zma": [21, 45, 66, 111],
"mii": {
"ltf": [2, 5, 3, 9, 21]
},
"fv": [1, 3, 6, 9]
},
"rmk": {
"amr": [50, 50, 100, 116, 150, 250]
}
},
"fik": {
"er": [592, 92, 32, 13],
"gp": [12, 34, 116, 29]
}
};
console.log(findRootProperty(object,116))

Related

intersecting multidimensional array in javascript

let say i have 1 multidimensional array and i want to exclude values that not equal in javascript.
here is the example array.
var filter = ["big_number", "odds_number"];
var arrays = {
"first" : {
"big_number" : [50,51,52],
"odds_number" : [39,41,51,53]
},
"second" : {
"big_number" : [61,62,63,64,65,70,72,73],
"odds_number" : [13,15,17,19,61,63,65,73]
}
};
i want to convert that array to be like this.
var new_arrays = {
"first" : [51],
"second" : [61,63,65,73]
};
here is my code
var newArray = {
"first" : [],
"second" : []
};
for (var k in arrays){
if (arrays.hasOwnProperty(k)) {
for(var f=0; f<filter.length; f++) {
newArray[k].push(arrays[k][filter[f]].filter(value => -1 !== arrays[k][filter[f]].indexOf(value))));
}
}
}
console.log(newArray);
actually i could do this code
var newArray = {
"first" : [],
"second" : []
};
for (var k in arrays){
if (arrays.hasOwnProperty(k)) {
newArray[k].push(arrays[k]["big_number"].filter(value => -1 !== arrays[k]["odds_number"].indexOf(value))));
}
}
console.log(newArray);
but i need to convert it through filter variable.
i could not use filter[0] and filter[1], because that values could change dynamically and could be more than 2 values in array.
You could loop through the keys and update the values using filter and includes:
var arrays={"first":{"big_number":[50,51,52],"odds_number":[39,41,51,53]},"second":{"big_number":[61,62,63,64,65,70,72,73],"odds_number":[13,15,17,19,61,63,65,73]}};
for (let key in arrays) {
arrays[key] = arrays[key]["big_number"]
.filter(n => arrays[key]["odds_number"].includes(n));
}
console.log(arrays)
If you don't want to mutate the original object then use Object.entries and reduce:
var arrays={"first":{"big_number":[50,51,52],"odds_number":[39,41,51,53]},"second":{"big_number":[61,62,63,64,65,70,72,73],"odds_number":[13,15,17,19,61,63,65,73]}};
const newObject = Object.entries(arrays).reduce((r, [key, {big_number, odds_number}]) => {
r[key] = big_number.filter(n => odds_number.includes(n));
return r
}, {})
console.log(newObject)
If you have more than 2 array properties, you can do something like this: Get all the arrays using Object.values and then use reduce to run the previous code recursively
var arrays = {
"first": {
"big_number": [50, 51, 52],
"odds_number": [39, 41, 51, 53],
"another_key": [41, 51, 53]
},
"second": {
"big_number": [61, 62, 63, 64, 65, 70, 72, 73],
"odds_number": [13, 15, 17, 19, 61, 63, 65, 73],
"another_key": [63, 65]
}
};
for (let key in arrays) {
arrays[key] = Object.values(arrays[key])
.reduce((a, b) => a.filter(c => b.includes(c)))
}
console.log(arrays)
Here is a little intersection snippet:
function intersect(a,b){
b.slice()
return a.filter(item=>{
if(b.includes(item)){
b.splice(b.indexOf(item),1)
return true
}
})
}
Using that, you can do this easily:
function intersect(a,b){
b.slice()
return a.filter(item=>{
if(b.includes(item)){
b.splice(b.indexOf(item),1)
return true
}
})
}
var filter = ["big_number", "odds_number"];
var output={}
var arrays = {
"first" : {
"big_number" : [50,51,52],
"odds_number" : [39,41,51,53]
},
"second" : {
"big_number" : [61,62,63,64,65,70,72,73],
"odds_number" : [13,15,17,19,61,63,65,73]
}
};
for(x in arrays){
output[x]=arrays[x][filter[0]]
for(let i=1;i<filter.length;i++){
output[x]=intersect(output[x],arrays[x][filter[i]])
}
}
console.log (output)
use Object.entries to get keys and values and then use reduce
var arrays = {
"first" : {
"big_number" : [50,51,52],
"odds_number" : [39,41,51,53]
},
"second" : {
"big_number" : [61,62,63,64,65,70,72,73],
"odds_number" : [13,15,17,19,61,63,65,73]
}
};
const output =Object.entries(arrays).reduce((accu, [key, {big_number}]) => {
if(!accu[key]) accu[key] = [];
big_number.forEach(num => {
if(num%2 !==0)
accu[key].push(num);
})
return accu;
}, {});
console.log(output);
You can get the unique values from both the arrays using Set and then using filter get only the common values.
var arrays = {"first": {"big_number": [50, 51, 52],"odds_number": [39, 41, 51, 53]},"second": {"big_number": [61, 62, 63, 64, 65, 70, 72, 73],"odds_number": [13, 15, 17, 19, 61, 63, 65, 73]}},
result = Object.keys(arrays).reduce((r,k) => {
let setB = new Set(arrays[k]["big_number"]);
r[k] = [...new Set(arrays[k]["odds_number"])].filter(x => setB.has(x));
return r;
},{});
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }

Split Javascript array elements into chunks at designated indexes

I have an array like so
const arr = [3,6,9,12,18,21,24,27,33,36];
I want the array arr split into chunks at 12, 21 and 33. That is at the index 3, 5, and 8. I want to produce another array chunks looking like this..
const chunks = [[3,6,9,12],[18,21],[24,27,33],[36]];
The solutions I have seen here basically split arrays into 'n' chunks. Basically I want to split at arrays at several (specified) indexes.
I do not mind an underscore.js/lodash solution. Thanks
You could use reduceRight and decide which elements to split at. Since you’re providing the last values of a sub-array rather than the first ones, going from right to left is actually a bit easier, hence I use a reduceRight rather than a reduce.
Split by value
const arr = [3, 6, 9, 12, 18, 21, 24, 27, 33, 36],
splitValues = [12, 21, 33],
chunks = arr.reduceRight((result, value) => {
result[0] = result[0] || [];
if (splitValues.includes(value)) {
result.unshift([value]);
} else {
result[0].unshift(value);
}
return result;
}, []);
console.log(chunks);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Split by index
const arr = [3, 6, 9, 12, 18, 21, 24, 27, 33, 36],
splitIndexes = [3, 5, 8],
chunks = arr.reduceRight((result, value, index) => {
result[0] = result[0] || [];
if (splitIndexes.includes(index)) {
result.unshift([value]);
} else {
result[0].unshift(value);
}
return result;
}, []);
console.log(chunks);
.as-console-wrapper { max-height: 100% !important; top: 0; }
const arr = [3,6,9,12,18,21,24,27,33,36];
// Important: this array gets mutated. Make a copy if that's not okay.
const inds = [3,5,8];
const chunked = arr.reduce((p, c, i) => { if (i-1 === inds[0]) { inds.shift(); p.push([]); } p[p.length-1].push(c); return p; }, [[]]);
console.log(chunked)
Here's an alternative way of doing it that I think is a bit clearer.
function chunkIt(arr, indexes) {
const ret = [];
let last = 0;
indexes.forEach(i => {
ret.push(arr.slice(last, i + 1));
last = i + 1;
});
if (last < arr.length) {
ret.push(arr.slice(last));
}
return ret;
}
console.log(chunkIt([3,6,9,12,18,21,24,27,33,36], [3,5,8]));
A bit "simplified" version with the reversed indexes, but splice modifies the source array:
arr = [3, 6, 9, 12, 18, 21, 24, 27, 33, 36]
chunks = [9, 6, 4, 0].map(i => arr.splice(i)).reverse()
console.log(JSON.stringify(chunks))
or slice can be used instead to preserve the source array:
arr = [3, 6, 9, 12, 18, 21, 24, 27, 33, 36], indexes = [0, 4, 6, 9]
chunks = indexes.map((e, i) => arr.slice(e, indexes[i + 1]))
console.log(JSON.stringify(chunks))

Convert array of objects to an object of arrays

I have the following structure
var nights = [
{ "2016-06-25": 32, "2016-06-26": 151, "2016-06-27": null },
{ "2016-06-24": null, "2016-06-25": null, "2016-06-26": null },
{ "2016-06-26": 11, "2016-06-27": 31, "2016-06-28": 31 },
];
And I want to transform it to:
{
"2016-06-24": [null],
"2016-06-25": [32, null],
"2016-06-26": [151, null, 11],
"2016-06-27": [null, 31],
"2016-06-28": [31]
}
What's the shortest way to solve this? I have no problems with using Underscore, Lodash or jQuery.
My current code is:
var out = {};
for (var i = 0; i < nights.length; i++) {
for (var key in nights[i]) {
if (out[key] === undefined) {
out[key] = [];
}
out[key].push(nights[i][key]);
}
}
It's similar to Convert array of objects to object of arrays using lodash but that has all keys present in each object.
You can do it with the following snippet (no need for lodash etc):
const x = [{ '2016-06-25': 32, '2016-06-26': 151, '2016-06-27': null }, { '2016-06-24': null, '2016-06-25': null, '2016-06-26': null }, { '2016-06-26': 11, '2016-06-27': 31, '2016-06-28': 31 }, ];
let y = {};
x.forEach(obj => {
Object.keys(obj).forEach(key => {
y[key] = (y[key] || []).concat([obj[key]]);
});
});
console.log(y)
Here is my one-liner. Uses map to map keys into array of objects with properties of the keys. Then maps the original array to only that property and sets it as the value of the property. One issue with this way is that it will only use the properties of the first object in the array. So if other objects have properties that aren't in the first object they will be ignored.
const output = Object.assign({}, ...Object.keys(input[0]).map(props => ({[props]: input.map(prop => prop[props])})))
Edit: the output was in the wrong format, fixed
You could iterate over the array and the over the keys of the object and build a new object with the keys as new keys.
var data = [{ '2016-06-25': 32, '2016-06-26': 151, '2016-06-27': null }, { '2016-06-24': null, '2016-06-25': null, '2016-06-26': null }, { '2016-06-26': 11, '2016-06-27': 31, '2016-06-28': 31 }, ],
result = {};
data.forEach(function (o) {
Object.keys(o).forEach(function (k) {
result[k] = result[k] || [];
result[k].push(o[k]);
});
});
console.log(result);
Used Typescript -- obviously you can remove the types while working in JavaScript.
Assumption: The array of objects coming into the function will always have the same kind and number of object keys
mapperArrayOfJsonsToJsonOfArrays(inputArrayOfJsons: any): any {
if (inputArrayOfJsons.length > 0) {
let resultMap = {};
let keys: any[] = Object.keys(inputArrayOfJsons[0]);
keys.forEach((key: any) => {
resultMap[key] = [];
});
inputArrayOfJsons.forEach((element: any) => {
let values: any[] = Object.values(element);
let index = 0;
values.forEach((value: any) => {
resultMap[keys[index]].push(value);
index = index + 1;
});
});
return resultMap;
}
return {};
}

sort 2 array with the values of one of them in javascript

i have two array, lets say
priceArray= [1,5,3,7]
userIdArray=[11, 52, 41, 5]
i need to sort the priceArray, so that the userIdArray will be also sorted.
for example the output should be:
priceArray= [1,3,5,7]
userIdArray=[11, 41, 52, 5]
any ideas how to do it?
i am writing my server in NodeJS
Taken from Sorting with map and adapted for the userIdArray:
// the array to be sorted
var priceArray = [1, 5, 3, 7],
userIdArray = [11, 52, 41, 5];
// temporary array holds objects with position and sort-value
var mapped = priceArray.map(function (el, i) {
return { index: i, value: el };
});
// sorting the mapped array containing the reduced values
mapped.sort(function (a, b) {
return a.value - b.value;
});
// container for the resulting order
var resultPrice = mapped.map(function (el) {
return priceArray[el.index];
});
var resultUser = mapped.map(function (el) {
return userIdArray[el.index];
});
document.write('<pre>' + JSON.stringify(resultPrice, 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(resultUser, 0, 4) + '</pre>');
With proper data structure, as rrowland suggest, you might use this:
var data = [{
userId: 11, price: 1
}, {
userId: 52, price: 15
}, {
userId: 41, price: 13
}, {
userId: 5, price: 17
}];
data.sort(function (a, b) {
return a.price - b.price;
});
document.write('<pre>' + JSON.stringify(data, 0, 4) + '</pre>');
A bit shorter with ES6
var priceArray = [1, 5, 3, 7],
userIdArray = [11, 52, 41, 5],
temp = Array.from(priceArray.keys()).sort((a, b) => priceArray[a] - priceArray[b]);
priceArray = temp.map(i => priceArray[i]);
userIdArray = temp.map(i => userIdArray[i]);
console.log(priceArray);
console.log(userIdArray);
.as-console-wrapper { max-height: 100% !important; top: 0; }
It's hard to prescribe a better solution without knowing the whole use-case. That said, if you need these sorted by ID, it may make more sense to create a single array that contains user objects:
var users = [
{ id: 123, price: 25.00 },
{ id: 124, price: 50.00 }
];
users.sort(function(a, b) {
return a.id - b.id;
});
Or, if they don't need to be sorted, you can simply create a map of users by id:
var userPrices = {
123: 25.00,
124: 50.00
};
Building on Rrowland's answer, you can create the array of objects with a library like lodash:
var prices = [1, 5, 8, 2];
var userIds = [3, 5, 1, 9];
var pairs = _.zipWith(prices, userIds, function(p, u) {
return { price: p, userId: u };
});
This will give you an object like:
[
{ price: 1, userId: 3 },
{ price: 5, userId: 5 },
... etc
]
Then, for sorting, you can simply use a Javascript sort:
pairs.sort(function(p) { return p.price });
If you really need it as an array of userIds, you can get it back, after the sort:
var sortedUserId = pairs.map( function(p) { return p.userId });
// returns [ 3, 9, 5, 8 ];
I have seen a nice talk about making impossible state impossible. This covered the 2 arrays that are related but can go out of sync and better to use one array of objects that have 2 properties (as mentioned several times).
However; if you want to mutate both arrays and sort them the same way you can do the following:
//this will mutate both arrays passed into it
// you could return the arrays but then you need to do arr.slice(0).sort(...) instead
const sortSame = sortFn => (arrayToSort,arrayToSortSame) => {
const sortResults = [];
arrayToSort.sort(//will mutate the array
(a,b)=>{
const result = sortFn(a,b);
sortResults.push(result);
return result
}
);
arrayToSortSame.sort(()=>sortResults.shift());
return undefined;
}
const priceArray= [1,5,3,7];
const userIdArray=[11, 52, 41, 5];
const numSortSameAscending = sortSame((a,b)=>a-b);
numSortSameAscending(priceArray,userIdArray);
console.log(
priceArray,userIdArray
)
Even though the code in this answer may look simpler it is not the cheapest way to do it, as mapping is a cheaper operation than sorting (better to map 3 times and sort once then to sort twice) depending on the size of the arrays and how much the original array is out of order this way of sorting same may be very expensive.

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