I have this object which its keys are guaranteed sorted and will be used for the operation. And each of its value is a 2d array.
var obj = {
"0": [
[0, 1], [0, 3], [0, 4]
],
"1": [
[1, 2], [1, 3]
],
"2": [
[2, 3], [2, 5]
],
"3": [
[3, 4], [3, 6]
],
"5": [
[5, 6]
],
"6": [
[6, 5]
]
}
I am trying to concatenate them and for each of its last value of the array is the next index of the object. So, my expected result is an array like this,
The pattern is, I have to find a way from 0 which is the first index of obj, to the last index which is 6 by using the values in each of it and linking its last array value to the next object. If that makes sense.
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 6]
[0, 1, 2, 5, 6]
[0, 1, 3, 4, 5, 6]
[0, 1, 3, 4]
[0, 1, 3, 6]
[0, 3, 4, 5, 6]
[0, 3, 6]
[0, 4]
This is my code so far, as I don't know how to proceed further..
var result = [];
for (var key in obj) {
var myarr = obj[key];
for (var i = 0; i < myarr.length; i++) {
result.push(myarr[i])
}
}
Any idea or feedback is welcome.
Edit
One of the expected result was [0, 1, 2, 3, 4, 5, 6], here's the step by step explanation.
The obj key starts from 0 and ends in 6, I have to form a way from 0 to 6 with the arrays in its value.
Starts from obj[0], the first array returns [0, 1], save this to res. (res is now [0, 1])
The last value of array in res is 1, now find the next value in obj[1]
obj[1] has two arrays, and ends with 2 or 3.. So it's possible to append with both of them, so it can be [0, 1, 2] or [0, 1, 3]. In this case, get the first one which is [1, 2] and append the last value to res. (res is now [0, 1, 2]).
The last value of array in res is now 2, now find the next value in obj[2].
obj[2] has two arrays, and ends with 3, or 5.. It's possible to append with both of them, so it can be [0, 1, 2, 3] or [0, 1, 2, 5]. In this case, get the first one which is [2, 3] and append the last value to res. (res is now [0, 1, 2, 3])
The last value of array in res is now 3, now find the next value in obj[3].
Repeat step 4 or 6. (res is now [0, 1, 2, 3, 4]).
The last value of array in res is now 4, now find the next value in obj[4].
Repeat step 4 or 6. (res is now [0, 1, 2, 3, 4, 5]).
The last value of array in res is now 5, now find the next value in obj[5].
Now value 6 is found which should be the end of iteration if you look at the step 4. Repeat step 4 or 6. (res is now [0, 1, 2, 3, 4, 5, 6]).
Repeat from step 1, and form another way to do it, with no duplicates of [0, 1, 2, 3, 4, 5 ,6].
This is a proposal, with a single extra output, mentioned below.
[
[0, 1, 2, 3, 4, 5, 6],
[0, 1, 2, 3, 6],
[0, 1, 2, 5, 6],
[0, 1, 3, 4, 5, 6], /* extended from below */
[0, 1, 3, 4], /* original result */
[0, 1, 3, 6],
[0, 3, 4, 5, 6], /* extended from below */
[0, 3, 4], /* extra line, line should not be in result */
[0, 3, 6], /* but follows the same building rule than above */
[0, 4]
]
Basically this solution is building a tree with the given information about linked nodes.
If some nodes are not contiguous, a backtracking is made for the missing links, with the above function for nodes, checkNodes or with iterPath, to walk the actual collected nodes for missing items.
function getParts(value, path, nodes) {
function checkNodes(a) {
if (a[1] === value + 1) {
getParts(a[1], path.concat(a[1]), nodes);
return true;
}
}
function iterPath(k) {
return (object[k] || []).some(function (a) {
return path[path.length - 1] + 1 === a[1] || iterPath(a[1]);
});
}
value = value || 0;
path = path || [value];
nodes = nodes || [];
if (object[value]) {
object[value].forEach(function (a, i, aa) {
if (a[1] === lastKey) {
parts.push(path.concat(a[1]));
return;
}
getParts(a[1], path.concat(a[1]), nodes.concat(aa.slice(i + 1)));
});
return;
}
if (nodes.some(checkNodes)) {
return;
}
path.slice(1).some(iterPath) && getParts(path[path.length - 1] + 1, path.concat(path[path.length - 1] + 1), nodes);
parts.push(path);
}
var object = {
0: [[0, 1], [0, 3], [0, 4]],
1: [[1, 2], [1, 3]],
2: [[2, 3], [2, 5]],
3: [[3, 4], [3, 6]],
5: [[5, 6]],
6: [[6, 5]]
},
lastKey = 6,
parts = [];
getParts();
parts.forEach(function (a) { console.log(JSON.stringify(a)); });
.as-console-wrapper { max-height: 100% !important; top: 0; }
Well, I was sitting on this for some time now, and sharing across my take on the problem:
The input object can be considered as an adjacency list of a tree:
var obj={0:[[0,1],[0,3],[0,4]],1:[[1,2],[1,3]],2:[[2,3],[2,5]],3:[[3,4],[3,6]],5:[[5,6]],6:[[6,5]]};
and the following as the result required, which is in fact, as I see it, the list of all root-to-leaf paths of the tree:
[0,1,2,3,4]
[0,1,2,3,6]
[0,1,2,5,6]
[0,1,3,4]
[0,1,3,6]
[0,3,4]
[0,3,6]
[0,4]
a little different than the result set mentioned in the question which is the below:
[0,1,2,3,4,5,6]
[0,1,2,3,6]
[0,1,2,5,6]
[0,1,3,4,5,6]
[0,1,3,4]
[0,1,3,6]
[0,3,4,5,6]
[0,3,6]
[0,4]
The difference between the results is only the question whether 4 and 6 are leaf nodes
Solution:
So I assume that for our Tree here:
0 is the root node
4 and 6 are the leaf nodes
See code below - I created a tree first, and from that listed out all the root to leaf paths:
// removed "6:[[6,5]]" as 6 is a 'leaf' of the tree
var obj={0:[[0,1],[0,3],[0,4]],1:[[1,2],[1,3]],2:[[2,3],[2,5]],3:[[3,4],[3,6]],5:[[5,6]]};
var availableNodes = Object.keys(obj);
var tree = availableNodes.reduce(function(hash) {
return function(prev, curr) {
hash[curr] = hash[curr] || {};
hash[curr].children = hash[curr].children || [];
obj[curr].forEach(function(element) {
hash[element[1]] = hash[element[1]] || {};
hash[element[1]].children = hash[element[1]].children || [];
hash[curr].rootPath = hash[curr].rootPath || [];
hash[curr].children.push({value: element[1],children: hash[element[1]].children});
});
curr && prev.push({value: curr,children: hash[curr].children});
return prev;
};
}(Object.create(null)), []);
//console.log(JSON.stringify(tree));
var result = [];
function rootToLeafPaths(node, path) {
path.push(+node.value);
if (node.children.length === 0) {
result.push(Array.from(path));
path.pop();
} else {
node.children.forEach(function(element) {
rootToLeafPaths(element, path);
});
path.pop();
}
}
rootToLeafPaths(tree[0], []);
console.log(JSON.stringify(result));
.as-console-wrapper{top:0;max-height:100%!important;}
Related
I attempted to ask a more complicated of this before but I couldn't explain it well so I am trying again with a simplified use case.
I will have an array of arrays like the following
var allData = [[1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5]]
I need to select 1 element from each array so that I get a unique set like [2,4,1,3,5] easy to do in this case as each array has all values. However this will rarely be the case. Instead I may have
var allData = [[1,2,4],[1,2],[1,2],[2,4,5],[1,2,3,5]]
In this case I couldn't pick 1 or 2 from the first array as that would prevent the 2nd and 3rd from having a unique combination. So something like [4,2,1,5,3] or [4,1,2,5,3] would be the only two possible answers for this combination.
The only way I see to do this is to just go through every combination but these will get fairly large so it doesn't seem reasonable as this happens real time. There are going to be at least 7 arrays, possibly 14 and distantly possible to have 31 so going through every combination would be fairly rough.
The 2nd part is if there is some way to "know" you have the best possible option. Say if there was some way I would know that having a single duplicate is my best case scenario. Even if I have to brute force it if I encounter a 1 duplication solution I would know to stop.
One easy way to get a very simple of this is to just subtract the number of possible choices from the number of elements but this is the correct answer in only the simplest of cases. Is there some type of library or anything to help solve these types of problems? It is a bit beyond my math abilities.
Here is something I have tried but it is too slow for larger sets and can fail. It works sometimes for the 2nd case I presented but only on luck
const allData = [[1,2,4],[1,2],[1,2],[2,4,5],[1,2,3,5]]
var selectedData = []
for (var i in allData){
console.log("length",allData[i].length)
var j = 0
while(j < allData[i].length){
console.log("chekcing",allData[i][j])
if (selectedData.includes(allData[i][j])){
console.log("removing item")
allData[i].splice(j,1)
}
else{j++}
}
var uniqueIds = Object.keys(allData[i])
console.log(uniqueIds)
var randId = Math.floor(Math.random() * uniqueIds.length)
console.log(randId)
selectedData.push(allData[i][randId])
console.log("selectedData",selectedData)
}
You can start with a fairly simple backtracking algorithm:
function pick(bins, n = 0, res = {}) {
if (n === bins.length) {
return res
}
for (let x of bins[n]) {
if (!res[x]) {
res[x] = n + 1
let found = pick(bins, n + 1, res)
if (found)
return found
res[x] = 0
}
}
}
//
let a = [[1, 2, 4], [1, 2], [1, 2], [2, 4, 5], [1, 2, 3, 4]]
console.log(pick(a))
This returns a mapping item => bin index + 1, which is easy to convert back to an array if needed.
This should perform relatively well for N < 10, for more/larger bins you can think of some optimizations, for example, avoid the worst case scenario by sorting bins from smallest to longest, or, depending on the nature of elements, represent bins as bitmasks.
You could count all elements and take various comparison with same indices.
function x([...data]) {
while (data.some(Array.isArray)) {
const
counts = data.reduce((r, a, i) => {
if (Array.isArray(a)) a.forEach(v => (r[JSON.stringify(v)] = r[JSON.stringify(v)] || []).push(i));
return r;
}, {}),
entries = Object.entries(counts),
update = ([k, v]) => {
if (v.length === 1) {
data[v[0]] = JSON.parse(k);
return true;
}
};
if (entries.some(update)) continue;
const grouped = entries.reduce((r, [, a]) => {
const key = JSON.stringify(a);
r[key] = (r[key] || 0) + 1;
return r;
}, {});
Object.entries(grouped).forEach(([json, length]) => {
const indices = JSON.parse(json);
if (indices.length === length) {
let j = 0;
indices.forEach(i => data[i] = data[i][j++]);
return;
}
if (length === 1) {
const value = JSON.parse(entries.find(([_, a]) => JSON.stringify(a) === json)[0]);
indices.forEach(i => data[i] = data[i].filter(v => v !== value));
data[indices[0]] = value;
}
});
}
return data;
}
console.log(...x([[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]));
console.log(...x([[1, 2, 4], [1, 2], [1, 2], [2, 4, 5], [1, 2, 3, 5]]));
console.log(...x([[1, 2, 4], [1, 2], [1, 2], [2, 4, 5], [1, 2, 3, 5], [6, 7, 8, 9], [6, 7, 8, 9], [6, 7, 8, 10], [6, 7, 8, 10], [6, 7, 8, 10]]));
Here is an implementation based around counting occurrences across the arrays.
It first creates a map indexed by value counting the number of inner arrays each value occurs in. It then sorts by inner array length to prioritize shorter arrays, and then iterates over each inner array, sorting by occurrence and selecting the first non-duplicate with the lowest count, or, if there are no unique values, the element with the lowest count.
const
occurrencesAcrossArrays = (arr) =>
arr
.reduce((a, _arr) => {
[...new Set(_arr)].forEach(n => {
a[n] = a[n] || 0;
a[n] += 1;
});
return a;
}, {}),
generateCombination = (arr) => {
const dist = occurrencesAcrossArrays(arr)
return arr
.sort((a, b) => a.length - b.length)
.reduce((a, _arr) => {
_arr.sort((a, b) => dist[a] - dist[b]);
let m = _arr.find(n => !a.includes(n));
if (m !== undefined) {
a.push(m);
} else {
a.push(_arr[0]);
}
return a;
}, []);
};
console.log(generateCombination([[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]).toString());
console.log(generateCombination([[1, 2, 4], [1, 2], [1], [2, 4, 5], [1, 2, 3, 5]]).toString());
console.log(generateCombination([[1, 2, 4], [1, 2], [1, 2], [2, 4, 5], [1, 2, 3, 5], [6, 7, 8, 9], [6, 7, 8, 9], [6, 7, 8, 10], [6, 7, 8, 10], [6, 7, 8, 10]]).toString());
Edit
In response to your comment – The situation seems to be emerging because the values all have the same occurrence count and are sequential.
This can be solved by keeping a running count of each value in the result array, and sorting each inner array by both by this running occurrence count as well as the original distribution count.This adds complexity to the sort, but allows you to simply access the first element in the array (the element with the lowest rate of occurrence in the result with the lowest occurrence count across all arrays).
const
occurrencesAcrossArrays = (arr) =>
arr
.reduce((a, _arr) => {
[...new Set(_arr)].forEach(n => {
a[n] = a[n] || 0;
a[n] += 1;
});
return a;
}, {}),
generateCombination = (arr) => {
const dist = occurrencesAcrossArrays(arr)
return arr
.sort((a, b) => a.length - b.length)
.reduce((acc, _arr) => {
_arr.sort((a, b) => (acc.occurrences[a] || 0) - (acc.occurrences[b] || 0) || dist[a] - dist[b]);
let m = _arr[0]
acc.occurrences[m] = acc.occurrences[m] || 0;
acc.occurrences[m] += 1;
acc.result.push(m);
return acc;
}, { result: [], occurrences: {} })
.result; // return the .result property of the accumulator
};
console.log(generateCombination([[2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6]]).toString());
// 2,3,4,5,6,2,3
console.log(generateCombination([[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]).toString());
// 1,2,3,4,5
console.log(generateCombination([[1, 2, 4], [1, 2], [1], [2, 4, 5], [1, 2, 3, 5]]).toString());
// 1,2,4,5,3
console.log(generateCombination([[1, 2, 4], [1, 2], [1, 2], [2, 4, 5], [1, 2, 3, 5], [6, 7, 8, 9], [6, 7, 8, 9], [6, 7, 8, 10], [6, 7, 8, 10], [6, 7, 8, 10]]).toString());
//1,2,4,5,3,9,6,10,7,8
console.log(generateCombination([[1], [2, 3,], [3, 4, 5], [3, 4, 5, 6], [2, 3, 4, 5, 6, 7]]).toString());
// 1,2,4,6,7
A note on .reduce()
If you're having trouble getting your head around .reduce() you can rewrite all the instances of it in this example using .forEach() and declaring accumulator variables outside of the loop. (This will not always be the case, depending on how you manipulate the accumulator value within a reduce() call).
Example below:
const occurrencesAcrossArrays = (arr) => {
const occurrences = {};
arr.forEach(_arr => {
[...new Set(_arr)].forEach(n => {
occurrences[n] = occurrences[n] || 0;
occurrences[n] += 1;
});
});
return occurrences;
};
const generateCombination = (arr) => {
const dist = occurrencesAcrossArrays(arr);
const result = [];
const occurrences = {};
arr.sort((a, b) => a.length - b.length);
arr.forEach(_arr => {
_arr.sort((a, b) => (occurrences[a] || 0) - (occurrences[b] || 0) || dist[a] - dist[b]);
let m = _arr[0]
occurrences[m] = occurrences[m] || 0;
occurrences[m] += 1;
result.push(m);
});
return result;
};
console.log(generateCombination([[2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6]]).toString());
// 2,3,4,5,6,2,3
console.log(generateCombination([[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]).toString());
// 1,2,3,4,5
console.log(generateCombination([[1, 2, 4], [1, 2], [1], [2, 4, 5], [1, 2, 3, 5]]).toString());
// 1,2,4,5,3
console.log(generateCombination([[1, 2, 4], [1, 2], [1, 2], [2, 4, 5], [1, 2, 3, 5], [6, 7, 8, 9], [6, 7, 8, 9], [6, 7, 8, 10], [6, 7, 8, 10], [6, 7, 8, 10]]).toString());
//1,2,4,5,3,9,6,10,7,8
console.log(generateCombination([[1], [2, 3,], [3, 4, 5], [3, 4, 5, 6], [2, 3, 4, 5, 6, 7]]).toString());
// 1,2,4,6,7
You could solve this problem using a MILP-model. Here is one implementation in MiniZinc (data has been extended to seven days):
int: Days = 7;
int: Items = 5;
set of int: DAY = 1..Days;
set of int: ITEM = 1..Items;
array[DAY, ITEM] of 0..1: A = % whether item k is allowed on day i
[| 1, 1, 0, 1, 0
| 1, 1, 0, 0, 0
| 1, 1, 0, 0, 0
| 0, 1, 0, 1, 1
| 1, 1, 0, 0, 0
| 0, 1, 0, 1, 1
| 1, 1, 1, 0, 1 |];
array[DAY, ITEM] of var 0..1: x; % 1 if item selected k on day i, otherwise 0
array[DAY, DAY, ITEM] of var 0..1: w; % 1 if item k selected on both day i and day j, otherwise 0
% exactly one item per day
constraint forall(i in DAY)
(sum(k in ITEM)(x[i, k]) = 1);
% linking variables x and w
constraint forall(i, j in DAY, k in ITEM where i < j)
(w[i, j, k] <= x[i, k] /\ w[i, j, k] <= x[j, k] /\ w[i, j, k] >= x[i, k] + x[j, k] - 1);
% try to minimize duplicates and if there are duplicates put them as far apart as possible
var int: obj = sum(i, j in DAY, k in ITEM where i < j)(((Days - (j - i))^2)*w[i, j, k]);
solve minimize obj;
output
["obj="] ++ [show(obj)] ++
["\nitem="] ++ [show([sum(k in ITEM)(k*x[i, k]) | i in DAY])];
Running gives:
obj=8
item=[2, 1, 5, 4, 3, 2, 1]
The following package looks promising for a JavaScript implementation: https://www.npmjs.com/package/javascript-lp-solver
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I have array transformation required as below in JS.
Source Array
var numbers =
[
[0,0,4],
[0,1,9],
[0,2,16] ,
[0,2,7] ,
[0,2,5] ,
[1,0,1],
[1,1,2],
[1,1,4],
[1,2,3]
];
Here first value in the source array represents row of target array, second value represents column of target array.
So the expected result array looks like
var result = [
[4, 9, [16, 7, 5]],
[1, [2, 4], 3]
];
Note:
1.Source/Target array can contain N number of rows & columns.
You could reduce the array and check the target element and assign the value or take an array.
var numbers = [[0, 0, 4], [0, 1, 9], [0, 2, 16], [0, 2, 7], [0, 2, 5], [1, 0, 1], [1, 1, 2], [1, 1, 4], [1, 2, 3]],
result = numbers.reduce((r, [i, j, v]) => {
r[i] = r[i] || [];
r[i][j] = r[i][j] === undefined
? v
: [].concat(r[i][j], v);
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can do this using reduce method.
var numbers = [[0, 0, 4], [0, 1, 9], [0, 2, 16], [0, 2, 7], [0, 2, 5], [1, 0, 1], [1, 1, 2], [1, 1, 4], [1, 2, 3]];
const result = numbers.reduce((r, [row, col, val]) => {
if (!r[row]) r[row] = [];
const el = r[row][col];
if (el) {
if (Array.isArray(el)) el.push(val);
else r[row][col] = [el, val];
} else {
r[row][col] = val
}
return r;
}, []);
console.log(result);
I am trying to create a function that will get the items that cannot be seen on the 2nd or 3rd and upcoming arrays passed within the function.
Right now my function gets only the similar items. How can I make it get the difference (w/c are the items that doesn't exist to the 2nd and 3rd and proceeding arrays.
const callM = function(arrays) {
arrays = Array.prototype.slice.call(arguments);
let result = [];
for(let i = 1; i < arrays.length; i++){
for(let x = 0; x < arrays[i].length; x++){
if(arrays[0].includes(arrays[i][x])){
result.push(arrays[i][x]);
}
}
}
return result;
};
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10])); // -> must be [1, 3, 4]
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8])); // -> must be [3,4]
The logic right now is a bit off as it gets the opposite. How do i fix this?
Also is there a way to do this using Higher Order functions such as reduce or filter?
Thanks!
I'd think about this differently. As the difference between two sets: array 0 and array 1...n
To get array 0, just shift it off the top
const arr0 = arrays.shift()
Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
This removes the first array from arrays
Next we combine the remaining arrays
const arrN = arrays.reduce(function(prev, curr) {
return prev.concat(curr)
})
Ref: http://www.jstips.co/en/javascript/flattening-multidimensional-arrays-in-javascript/
Unneeded, handled by includes as mentioned by #Phil
Next filter duplicates from arrN by comparing with itself
const unique = arrN.filter(function(elem, index, self) {
return index == self.indexOf(elem);
})
Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
Next filter with includes to find the difference (or union)
const diff = arr0.filter(function(item) {
return !arrN.includes(item))
}
Full snippet:
function callM(arrays) {
const arr0 = arrays.shift()
const arrN = arrays.reduce(function(prev, curr) {
return prev.concat(curr)
})
//const unique = arrN.filter(function(elem, index, self) {
// return index == self.indexOf(elem)
//})
return arr0.filter(function(item) {
return !arrN.includes(item)
})
}
console.log(callM([[1, 2, 3, 4, 5], [5, 2, 10]]))
console.log(callM([[1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8]]))
of course ES6 would be easier. ;)
const callM = (first, ...rest) => {
const arrays = [].concat(...rest)
return first.filter(item => !arrays.includes(item))
}
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10]))
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8]))
A short solution for small and medium sized arrays:
// Return elements in array but not in filters:
function difference(array, ...filters) {
return array.filter(el => !filters.some(filter => filter.includes(el)));
}
// Example:
console.log(difference([1, 2, 3, 4, 5], [5, 2, 10])); // [1, 3, 4]
console.log(difference([1, 2, 3, 4, 5], [5, 1, 10], [7, 2, 8])); // [3, 4]
For large inputs, consider creating a Set from all filters and filtering in linear time using set.has(el).
In order to fix your implementation, you could label the outer for-loop and continue from there whenever a filter contains one of the array elements. Only when all filters pass without match, you push the array element into the result:
// Return elements in array but not in filters:
function difference(array, ...filters) {
const result = [];
loop: for (const el of array) {
for (const filter of filters) {
if (filter.includes(el)) continue loop;
}
result.push(el);
}
return result;
}
// Example:
console.log(difference([1, 2, 3, 4, 5], [5, 2, 10])); // [1, 3, 4]
console.log(difference([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8])); // [3,4]
If you're willing to use Underscore, you can do this in one line of code:
console.log(_.difference([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8]))
https://jsfiddle.net/o1zuaa6m/
You can use array#reduce to create object lookup of all the other array excluding the first array. Then use array#filter to get the values which are not present in the object lookup
var callM = (first, ...rest) => {
var combined = rest
.reduce((res,arr) => res.concat(arr))
.reduce((o, v) => {
o[v] = true;
return o;
},{});
return first
.filter(v => !combined[v]);
}
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10])); // -> must be [1, 3, 4]
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8])); // -> must be [3,4]
The "proper" way to exclude values is usually to use a lookup hash set with the values to exclude:
const callM = (a, ...b) => (b = new Set(b.concat.apply(...b)), a.filter(v => !b.has(v)))
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10])); // [1, 3, 4]
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8])); // [3, 4]
For example: I have set 1,3,4,5,6,7,8,10,11
scenario 1 : subset = 1,3,4 which is in starting part of the series = Acceptable
scenario 2 : subset = 8,10,11 which is in ending part of the series = Acceptable
scenario 3 : subset = 1,3,10,11 which is in both starting and ending part of the series = Acceptable
scenario 4 : subset = 1,3,6,8 which is in starting part of series and also in the middle of the series = Not Acceptable.
Main thing to achieve is to check if the given series is in starting part of the series or ending part without breaking the series using jquery or javacript
Help will be much appreciated
Thanks in advance
You could use a two pass approach, with first collecting for all subarray items the comparison at the same index from start and end (later denoted as left and right side).
To indicate a result, 4 states are returned which mean
0 - no match, neither from the left nor from the right side matches the value,
1 - item of the left side of the array matches,
2 - item of the right side of the array matches,
3 - item matches on both sides.
After collecting the values which represent match and position, the values have to be consolidated to the wanted result, true or false.
To check each value, you need another value which represents the expected side. At start, the expected side is set to 1, because it starts at the left side to check.
The temporary result of the callback with Array#every is a single check if the actual value is greater or equal to the expected value. This includes indicator 3 because this value is in both sides. A value of zero exits the loop as well.
If a value of 2 is found, then it indicates the right side and all following elements have to be greater than 2, therefor the expected value is set to 2.
The presented solution could be shortened to just return the wanted boolean value.
function check(array, subarray) {
var expected = 1,
temp = subarray.map(function (a, i) {
var offset = array.length - subarray.length;
return (a === array[i]) + 2 * (a === array[i + offset]);
}),
result = temp.every(function (a) {
var r = a >= expected;
if (a === 2) {
expected = 2;
}
return r;
});
return temp.concat(result);
}
var array = [1, 3, 4, 5, 6, 7, 8, 10, 11];
console.log(check(array, [1, 3, 4])); // true
console.log(check(array, [8, 10, 11])); // true
console.log(check(array, [1, 3, 10, 11])); // true
console.log(check(array, [1, 3, 6, 8])); // false
console.log(check([1, 2, 3, 4, 5, 6], [1, 4, 3, 6])); // false
console.log(check([1, 2, 3, 4, 5, 6], [1, 4, 3, 6])); // false
console.log(check([1, 2, 3, 2, 3, 4], [3, 2, 3, 2])); // false
console.log(check([1, 2, 3, 2, 3, 4], [1, 2, 3, 4])); // true
.as-console-wrapper { max-height: 100% !important; top: 0; }
The short version without a temporary array.
function check(array, subarray) {
var offset = array.length - subarray.length,
expected = 1;
return subarray.every(function (a, i) {
var state = (a === array[i]) + 2 * (a === array[i + offset]),
result = state >= expected;
if (state === 2) {
expected = 2;
}
return result;
});
}
var array = [1, 3, 4, 5, 6, 7, 8, 10, 11];
console.log(check(array, [1, 3, 4])); // true
console.log(check(array, [8, 10, 11])); // true
console.log(check(array, [1, 3, 10, 11])); // true
console.log(check(array, [1, 3, 6, 8])); // false
console.log(check([1, 2, 3, 4, 5, 6], [1, 4, 3, 6])); // false
console.log(check([1, 2, 3, 4, 5, 6], [1, 4, 3, 6])); // false
console.log(check([1, 2, 3, 2, 3, 4], [3, 2, 3, 2])); // false
console.log(check([1, 2, 3, 2, 3, 4], [1, 2, 3, 4])); // true
.as-console-wrapper { max-height: 100% !important; top: 0; }
You would iterate through the subarray from left to right, and from right to left, and stop where the value does not match with the value in the main array at the "corresponding position", i.e. the position counting from the same side of the array.
If at the end of both loops all values of the subarray matched, i.e. the two indexes crossed each other, then the result is true.
Here is the code:
function isSubsetAtEnds(array, subarray) {
const diff = array.length - subarray.length;
let i, j;
if (diff < 0) return false;
for (i = 0; i < subarray.length; i++)
if (array[i] !== subarray[i]) break;
for (j = subarray.length - 1; j >= i; j--)
if (array[j+diff] !== subarray[j]) break;
return j < i;
}
var array = [1, 3, 4, 5, 6, 7, 8, 10, 11];
console.log(isSubsetAtEnds(array, [1, 3, 4])); // true
console.log(isSubsetAtEnds(array, [8, 10, 11])); // true
console.log(isSubsetAtEnds(array, [1, 3, 10, 11])); // true
console.log(isSubsetAtEnds(array, [1, 3, 6, 8])); // false
One simple approach: remove matching numbers from the beginning and end of both arrays, and see if there are any leftovers.
var checkIt = function(arr,subarr) {
// strip off matching numbers at the beginning
while (subarr.length && arr[0] === subarr[0]) {
arr.shift();
subarr.shift();
}
// strip off matching numbers at the end
while (subarr.length && arr[arr.length - 1] === subarr[subarr.length - 1]) {
arr.pop();
subarr.pop();
}
// if there aren't any leftovers, return true
return (subarr.length === 0)
}
console.log(checkIt( [1, 3, 4, 5, 6, 7, 8, 10, 11],[1, 3, 4]));
console.log(checkIt( [1, 3, 4, 5, 6, 7, 8, 10, 11],[8, 10, 11]));
console.log(checkIt( [1, 3, 4, 5, 6, 7, 8, 10, 11],[1, 3, 10, 11]));
console.log(checkIt( [1, 3, 4, 5, 6, 7, 8, 10, 11],[1, 3, 6, 8]));
// pathological edge case:
console.log(checkIt( [1,2,3,2,1],[1,2,3,2,1] ))
// This returns true, but based on the rules of the puzzle I'm honestly not sure whether that's correct.
I was playing around with JavaScript's array prototype map and I don't understand why I'm getting the following values:
console.log(
[1,2,2,5,6].map(function(a,b){
return a+b;
})
); /*This returns [ 1, 3, 4, 8, 10 ]*/
Shouldn't the above code return [1,3,4,7,11] ?
Here's another snippet which shows that elements in the array are added sequentially and are correct at least I believe so.
console.log(
[1,2,3,4,5,6].map(function(a,b){
return a+b;
})
); /*[ 1, 3, 5, 7, 9, 11 ]*/
This is just a curious question more along the lines of why the first snippet of code seems.
It's because map passes you the value as the first parameter and the index as the second. So, what you're adding is: 1 + 0, 2+1, 2+2, 5+3, etc.
Your a value is the value out of the array: 1, 2, 2, 5, 6
Your b value is actually an index, not a value out of the array: 0, 1, 2, 3, 4
Let's sum:
[1, 2, 2, 5, 6] // Your values
+ [0, 1, 2, 3, 4] // Indices
-----------------
[1, 3, 4, 8,10] // Result
[1, 2, 3, 4, 5, 6] // Your values
+ [0, 1, 2, 3, 4, 5] // Indices
--------------------
[1, 3, 5, 7, 9,11] // Result
The results are correct.
I think you are confusing map with reduce:
var arr = [];
[1,2,2,5,6].reduce(function(a,b){
arr.push(a+b);
return b;
}, 0);
arr; // [1, 3, 4, 7, 11]
[0, 1, 2, 2, 5] // Values returned in previous iteration
+ [1, 2, 2, 5, 6] // Your values
-----------------
[1, 3, 4, 7,11] // Result
so the first parameter 'a' is value and the 'b' is index. so adding together it shows the corect value only. so 'a' contains [1, 2, 2, 5, 6] and 'b' contains [0, 1, 2, 3, 4]