Finding an array of arrays... inside another array - javascript

I'm dealing with 'arrays of arrays' and trying to test if all 'sets' contained in the second array are present in the first array.
var arr = [['Netherlands','PP3a'],['Austria','PP16a'],['Estonia','PP3a'],['Luxembourg','PP3a'],['Belgium','PP3a']];
var n = [['Luxembourg','PP3a'],['Netherlands','PP3a'],['Belgium','PP3a']];
In my example https://jsfiddle.net/mnb8jddw/ they clearly are present, but the code (which incidentally seems to work with numbers), reads false. I've obviously got myself confused and would really appreciate some help as I suspect I'm taking the wrong approach.
var arr = [
['Netherlands', 'PP3a'],
['Austria', 'PP16a'],
['Estonia', 'PP3a'],
['Luxembourg', 'PP3a'],
['Belgium', 'PP3a']
];
var n = [
['Luxembourg', 'PP3a'],
['Netherlands', 'PP3a'],
['Belgium', 'PP3a']
];
function searchForArray(haystack, needle) {
var i, j, current;
for (var i in haystack) {
if (needle.length === haystack[i].length) {
current = haystack[i];
for (j = 0; j < needle.length && needle[j] === current[j]; ++j);
if (j === needle.length)
return i;
}
}
return -1;
}
console.log(searchForArray(arr, n)); // -1 = false

I'm not sure that it is the answer you are looking for, but if you are looking for a quick and dirty solution, you could try something like this:
const lookup = (ar, sets) => {
// concatenate each entry in the searched array
const _hashed = ar.map(i => i.join(''))
return sets.every((set) => {
// contatenate each entry to look for
const _set = set.join('')
// does the searched array contain the concatenated string?
return _hashed.indexOf(_set) > -1
})
}
console.log(lookup(arr, n)) // => true
Note that the order of the elements matters (ie: ['Luxembourg', 'PP3a'] will match, but ['PP3a', 'Luxembourg'] won't)
See updated fiddle

Related

what's the most efficient way to split an array of millions of data based on condition?

It goes something like this where I have a london array containing more than 10 million data
london = ['dwig7xmW','gIzbnHNI' ...]
And now I have a userTraveled which also contains millions of data
userTraveled = ['ntuJV09a' ...]
Now what's the most efficient way to split userTraveled into inLondon and notInLondon.
My attempt.
inLondon = []
notInLondon = []
userTraveled.forEach((p) => london.includes(p) ? inLondon.push(p) : notInLondon.push(p))
london.includes(p) will do a linear search over the array. Doing that for every userTraveled is horribly inefficient. Use a Set instead:
const usersInLondon = [], usersNotInLondon = [];
const lookup = new Set(london);
for (const p of usersTraveled) {
(lookup.has(p) ? usersInLondon : usersNotInLondon).push(p);
}
I can offer a O(n*log(n)) solution instead of your O(n^2), first order the passwords and later use the binary search on it instead of the include to search for an item
Hope it helps =)
const london = ['dwig7xmW','gIzbnHNI']
const userTraveled = ['ntuJV09a', 'dwig7xmW']
let inLondon = []
let notInLondon = []
const sortedlondon=london.sort();
userTraveled.forEach((p) => (binarySearch(sortedlondon,p)!=-1 ? inLondon.push(p) : notInLondon.push(p)))
//https://www.htmlgoodies.com/javascript/how-to-search-a-javascript-string-array-using-a-binary-search/
function binarySearch(items, value){
var startIndex = 0,
stopIndex = items.length - 1,
middle = Math.floor((stopIndex + startIndex)/2);
while(items[middle] != value && startIndex < stopIndex){
//adjust search area
if (value < items[middle]){
stopIndex = middle - 1;
} else if (value > items[middle]){
startIndex = middle + 1;
}
//recalculate middle
middle = Math.floor((stopIndex + startIndex)/2);
}
//make sure it's the right value
return (items[middle] != value) ? -1 : middle;
}
I hope you are not using these data in a wrong way.
const passwords = ['a', 'b']
const rawPasswords = ['c', 'b'];
const setPasswords = new Set(passwords)
const uniquePassword = [];
const usedPassword = [];
rawPasswords.forEach(rp => {
if (setPasswords.has(rp)) {
usedPassword.push(rp)
} else {
uniquePassword.push(rp)
}
})
console.log(uniquePassword, usedPassword)
Referring to this answer for performance tests: Get all unique values in a JavaScript array (remove duplicates) the best solution in your case would be to use an Object. Since you require to know about the duplicates and not just remove them.
function uniqueArray( ar ) {
var j = {};
var k = [];
var unique;
ar.forEach( function(v) {
if(j.hasOwnProperty(v)){
k.push(v);
} else {
j[v] = v;
}
});
unique = Object.keys(j).map(function(v){
return j[v];
});
return [unique, k];
}
var arr = [1, 1, 2, 3, 4, 5, 4, 3];
console.log(uniqueArray(arr));
First it loops through the input array and checks if the value is already existing as a key on the object. If that's not the case, it adds it. If it is, it pushes the value to another array. Since objects use a hash, the Javascript engine can work faster with it.
Secondly it goes through the object's keys to turn it back into an array and finally returns both. I didn't add this explanation because the provided reference already explained it.
The result will be an array containing 2 arrays. First the array with unique values, second the array with duplicates.

Find indices within a string where any combination of an array of words is found

Sample data:
String: "barfoofoobarthefoobarman"
Array of words: ["bar", "foo", "the"]
Output:
[6, 9, 12]
I was asked this question during an interview. Due to time constraint, I tried to find all the possible words that could be made out of the array of words (i. e. "barfoothe"), but was told that would not scale for large arrays. Was suggested to use a map data structure, but I think my solution doesn't scale either, and it's brute forced.
Here's the solution.
var solution = function(string, words) {
let output = [];
let wordsMap = new Map();
let wordsNumber = words.length;
let wordLength = words[0].length;
words.forEach((word) => {
if (!wordsMap.has(word))
wordsMap.set(word, 1);
else
wordsMap.set(word, wordsMap.get(word) + 1);
});
for (let i = 0; i <= string.length-(wordsNumber*wordLength); i+=wordLength) {
let tempMap = new Map(wordsMap);
let check = true;
let tempString = string.substring(i, i + wordsNumber*wordLength);
for (let j = 0; j <= tempString.length - wordLength; j += wordLength) {
let tempString2 = tempString.substring(j, j + wordLength);
if (tempMap.has(tempString2))
tempMap.set(tempString2, tempMap.get(tempString2) - 1);
}
for (let val of tempMap.values()){
if (val !== 0){
check = false
break;
}
}
if (check)
output.push(i)
}
console.log(output);
}
solution("barfoothefoobarman", ["foo", "bar"]);
Any suggestion for a smarter solution?
You could create a dynamic regular expression.
const words = ['foo', 'bar']
const rx = new RegExp(words.join('|'), 'g')
// todo escape special characters
Then search away.
const counts = words.map(it=>0) // [0,0]
// todo use map or object to track counts instead of array
while (m = rx.exec(inputString)) {
const index = words.indexOf(m[0])
counts[index]++
}
Thank you for your question. I think the question in the interview was less about the right solution and more about the right approach.
The trickiest part is actually just finding the word combinations. There are several approaches here. For me it's a clear case for recursion.
So my approach would be:
find all word combinations, except combinations with itself (for example: foofoo or barbar).
iterate through the word combinations and ask whether they are contained in the string.
extra: Sort SolutionArray
Done!
Note: I use indexOf() for point 2 but I think a regex match would make it even better because you find all possibilities of a word in a string and not just the first one like with indexOf. Would make sense for longer strings.
const arr = ["foo", "bar"];
const str = "barfoothefoobarman"
let res = [];
const combinations = (len, val, existing) => {
if (len == 0) {
res.push(val);
return;
}
for(let i=0; i<arr.length; i++) {
if(! existing[i]) {
existing[i] = true;
combinations(len-1, val + arr[i], existing);
existing[i] = false;
}
}
}
const buildCombinations = (arr = []) => {
for(let i = 0; i < arr.length; i++) {
combinations(arr.length - i, "", []);
}
};
buildCombinations(arr);
// exclude the base wordes from result array
newRes = res.filter((e) => {
if (! arr.includes(e)) {
return e;
}
})
console.log('all word combinations:', newRes);
// get the string position
const _positions = [];
newRes.forEach((w) => {
let res = str.indexOf(w);
if (res != -1 && ! _positions.includes(res)) {
_positions.push(res);
}
})
// sort array and use Float64Array to speed up
const positions = new Float64Array(_positions)
console.log('positions', positions.sort())

Checking whether the number of unique numbers within array exceeds n

Just as title reads, I need to check whether the number of unique entries within array exceeds n.
Array.prototype.some() seems to fit perfectly here, as it will stop cycling through the array right at the moment, positive answer is found, so, please, do not suggest the methods that filter out non-unique records and measure the length of resulting dataset as performance matters here.
So far, I use the following code, to check if there's more than n=2 unique numbers:
const res = [1,1,2,1,1,3,1,1,4,1].some((e,_,s,n=2) => s.indexOf(e) != s.lastIndexOf(e) ? false : n-- ? false : true);
console.log(res);
.as-console-wrapper { min-height: 100%}
And it returns false while there's, obviously 3 unique numbers (2,3,4).
Your help to figure out what's my (stupid) mistake here is much appreciated.
p.s. I'm looking for a pure JS solution
You can use a Map() with array values as map keys and count as values. Then iterate over map values to find the count of unique numbers. If count exceeds the limit return true, if not return false.
Time complexity is O(n). It can't get better than O(n) because every number in the array must be visited to find the count of unique numbers.
var data = [1, 1, 2, 1, 1, 3, 1, 1, 4, 1];
function exceedsUniqueLimit(limit) {
var map = new Map();
for (let value of data) {
const count = map.get(value);
if (count) {
map.set(value, count + 1);
} else {
map.set(value, 1);
}
}
var uniqueNumbers = 0;
for (let count of map.values()) {
if (count === 1) {
uniqueNumbers++;
}
if (uniqueNumbers > limit) {
return true;
}
}
return false;
}
console.log(exceedsUniqueLimit(2));
To know if a value is unique or duplicate, the whole array needs to be scanned at least once (Well, on a very large array there could be a test to see how many elements there is left to scan, but the overhead for this kind of test will make it slower)
This version uses two Set
function uniqueLimit(data,limit) {
let
dup = new Set(),
unique = new Set(),
value = null;
for (let i = 0, len = data.length; i < len; ++i) {
value = data[i];
if ( dup.has(value) ) continue;
if ( unique.has(value) ) {
dup.add(value);
unique.delete(value);
continue;
}
unique.add(value);
}
return unique.size > limit;
}
I also tried this version, using arrays:
function uniqueLimit(data, limit) {
let unique=[], dup = [];
for (let idx = 0, len = data.length; idx < len; ++idx) {
const value = data[idx];
if ( dup.indexOf(value) >= 0 ) continue;
const pos = unique.indexOf(value); // get position of value
if ( pos >= 0 ) {
unique.splice(pos,1); // remove value
dup.push(value);
continue;
}
unique.push(value);
}
return unique.length > limit;
};
I tested several of the solutions in this thread, and you can find the result here. If there are only a few unique values, the method by using arrays is the fastest, but if there are many unique values it quickly becomes the slowest, and on large arrays slowest by several magnitudes.
More profiling
I did some more tests with node v12.10.0. The results are normalized after the fastest method for each test.
Worst case scenario: 1000000 entries, all unique:
Set 1.00 // See this answer
Map 1.26 // See answer by Nikhil
Reduce 1.44 // See answer by Bali Balo
Array Infinity // See this answer
Best case scenario: 1000000 entries, all the same:
Array 1.00
Set 1.16
Map 2.60
Reduce 3.43
Question test case: [1, 1, 2, 1, 1, 3, 1, 1, 4, 1]
Array 1.00
Map 1.29
Set 1.47
Reduce 4.25
Another test case: [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,
1,1,1,1,1,1,1,3,4,1,1,1,1,1,1,1,2,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,5 ]
Array 1.00
Set 1.13
Map 2.24
Reduce 2.39
Conclusion
The method that uses Set works for both small and large arrays, and performs well regardless of if there are many unique values or not. The version that are using arrays can be faster if there are few unique values, but quickly becomes very slow if there are many unique values.
Using sets, We count hypothetical unique set size and duplicateSet size and delete unique set element for each duplicate found. If unique set size goes below n, we stop iterating.
function uniqueGtN(res, n) {
let uniqSet = new Set(res);
let max = uniqSet.size;
if (max <= n) return false;
let dupSet = new Set();
return !res.some(e => {
if (dupSet.has(e)) {
if (uniqSet.has(e)) {
uniqSet.delete(e);
console.log(...uniqSet);
return (--max <= n);
}
} else {
dupSet.add(e);
}
});
}
console.log(uniqueGtN([1, 1, 2, 1, 1, 3, 3, 1], 2));
From your original solution, I have changed few things, it seems to be working fine:
(function() {
const array = [1,1,2,1,1,3,1,1,4,1];
function hasExceedingUniqueNumber(array, number) {
return array.some((e,_,s,n=number) => {
let firstIndex = s.indexOf(e);
let lastIndex = s.lastIndexOf(e);
// NOT unique
if (firstIndex != lastIndex) {
return false;
}
// unique
return e > n;
});
}
console.log('1', hasExceedingUniqueNumber(array, 1));
console.log('2', hasExceedingUniqueNumber(array, 2));
console.log('3', hasExceedingUniqueNumber(array, 3));
console.log('4', hasExceedingUniqueNumber(array, 4));
})();
So the shorter version looks like this:
(function() {
const array = [1,1,2,1,1,3,1,1,4,1];
function hasExceedingUniqueNumber(array, number) {
return array.some((e,_,s,n=number) => s.indexOf(e) != s.lastIndexOf(e) ? false : e > n);
}
console.log('1', hasExceedingUniqueNumber(array, 1));
console.log('2', hasExceedingUniqueNumber(array, 2));
console.log('3', hasExceedingUniqueNumber(array, 3));
console.log('4', hasExceedingUniqueNumber(array, 4));
})();
The code listed in your question does not work because m is not shared across the calls to the some callback function. It is a parameter, and its value is 2 at each iteration.
To fix this, either put m outside, or use the thisArg of the some function (but that means you can't use an arrow function)
let m = 2;
const res = [1,1,1,2,1,1,3,1,1,1,4,1,1]
.sort((a,b) => a-b)
.some((n,i,s) => i > 0 && n == s[i-1] ? !(m--) : false);
// ----- or -----
const res = [1,1,1,2,1,1,3,1,1,1,4,1,1]
.sort((a,b) => a-b)
.some(function(n,i,s) { return i > 0 && n == s[i-1] ? !(this.m--) : false; }, { m: 2 });
Note: this code seems to count if the number of duplicates exceeds a certain value, not the number of unique values.
As another side note, I know you mentioned you did not want to use a duplicate removal algorithm, but performant ones (for example hash-based) would result in something close to O(n).
Here is a solution to count all the values appearing exactly once in the initial array. It is a bit obfuscated and hard to read, but you seem to be wanting something concise. It is the most performant I can think of, using 2 objects to store values seen at least once and the ones seen multiple times:
let res = [1,1,2,3,4].reduce((l, e) => (l[+!l[1][e]][e] = true, l), [{},{}]).map(o => Object.keys(o).length).reduce((more,once) => once-more) > 2;
Here is the less minified version for people who don't like the short version:
let array = [1,1,2,3,4];
let counts = array.reduce((counts, element) => {
if (!counts.atLeastOne[element]) {
counts.atLeastOne[element] = true;
} else {
counts.moreThanOne[element] = true;
}
return counts;
}, { atLeastOne: {}, moreThanOne: {} });
let exactlyOnceCount = Object.keys(counts.atLeastOne).length - Object.keys(counts.moreThanOne).length;
let isOverLimit = exactlyOnceCount > 2;
Whenever I have a type of problem like this, I always like to peek at how the underscore JS folks have done it.
[Ed again: removed _.countBy as it isn't relevant to the answer]
Use the _.uniq function to return a list of unique values in the array:
var u = _.uniq([1,1,2,2,2,3,4,5,5]); // [1,2,3,4,5]
if (u.length > n) { ...};
[ed:] Here's how we might use that implementation to write our own, opposite function that returns only non-unique collection items
function nonUnique(array) {
var result = [];
var seen = [];
for (var i = 0, length = array.length; i < length; i++) {
var value = array[i];
if (seen.indexOf(value) === -1) { // warning! naive assumption
seen.push(value);
} else {
result.push(value);
}
}
console.log("non-unique result", result);
return result;
};
function hasMoreThanNUnique(array, threshold) {
var uArr = nonUnique(array);
var accum = 0;
for (var i = 0; i < array.length; i++) {
var val = array[i];
if (uArr.indexOf(val) === -1) {
accum++;
}
if (accum > threshold) return true;
}
return false;
}
var testArrA = [1, 1, 2, 2, 2, 3, 4, 5]; // unique values: [3, 4, 5]
var testArrB = [1, 1, 1, 1, 4]; // [4]
var testResultsA = hasMoreThanNUnique(testArrA, 3)
console.log("testArrA and results", testResultsA);
var testResultsB = hasMoreThanNUnique(testArrB, 3);
console.log("testArrB and results", testResultsB);
So far, I came up with the following:
const countNum = [1,1,1,2,1,1,3,1,1,1,4,1,1].reduce((r,n) => (r[n]=(r[n]||0)+1, r), {});
const res = Object.entries(countNum).some(([n,q]) => q == 1 ? !(m--) : false, m=2);
console.log(res);
.as-console-wrapper{min-height:100%}
But I don't really like array->object->array conversion about that. Is there a faster and (at the same time compact) solution?

How to adjust return values of map() function?

I have been trying to make a excercise in the course I am taking. At the end, I did what was asked, but I personally think I overdid too much and the output is not convenient -- it's a nested array with some blank arrays inside...
I tried to play with return, but then figured out the problem was in the function I used: map always returns an array. But all other functions, which are acceptable for arrays (in paticular forEach and I even tried filter) are not giving the output at all, only undefined. So, in the end, I have to ask you how to make code more clean with normal output like array with just 2 needed numbers in it (I can only think of complex way to fix this and it'll add unneeded junk to the code).
Information
Task:
Write a javascript function that takes an array of numbers and a target number. The function should find two different numbers in the array that, when added together, give the target number. For example: answer([1,2,3], 4) should return [1,3]
Code
const array1 = [1, 2, 3];
const easierArray = [1, 3, 5] //Let's assume number we search what is the sum of 8
const findTwoPartsOfTheNumber = ((arr, targetNum) => {
const correctNumbers = arr.map((num, index) => {
let firstNumber = num;
// console.log('num',num,'index',index);
const arrayWeNeed = arr.filter((sub_num, sub_index) => {
// console.log('sub_num',sub_num,'sub_index',sub_index);
if (index != sub_index && (firstNumber + sub_num) === targetNum) {
const passableArray = [firstNumber, sub_num] //aka first and second numbers that give the targetNum
return sub_num; //passableArray gives the same output for some reason,it doesn't really matter.
}
})
return arrayWeNeed
})
return correctNumbers;
// return `there is no such numbers,that give ${targetNum}`;
})
console.log(findTwoPartsOfTheNumber(easierArray, 8));
console.log(findTwoPartsOfTheNumber(array1, 4));
Output
[[],[5],[3]]
for the first one
You can clean up the outpu by flatting the returned arrays :
return arrayWeNeed.flat();
and
return correctNumbers.flat();
const array1 = [1, 2, 3];
const easierArray = [1, 3, 5] //Let's assume number we search what is the sum of 8
const findTwoPartsOfTheNumber = ((arr, targetNum) => {
const correctNumbers = arr.map((num, index) => {
let firstNumber = num;
// console.log('num',num,'index',index);
const arrayWeNeed = arr.filter((sub_num, sub_index) => {
// console.log('sub_num',sub_num,'sub_index',sub_index);
if (index != sub_index && (firstNumber + sub_num) === targetNum) {
const passableArray = [firstNumber, sub_num] //aka first and second numbers that give the targetNum
return sub_num; //passableArray gives the same output for some reason,it doesn't really matter.
}
})
return arrayWeNeed.flat();
})
return correctNumbers.flat();
// return `there is no such numbers,that give ${targetNum}`;
})
console.log(findTwoPartsOfTheNumber(easierArray, 8));
console.log(findTwoPartsOfTheNumber(array1, 4));
However, using a recursive function could be simpler :
const answer = (arr, num) => {
if (arr.length < 1) return;
const [first, ...rest] = arr.sort();
for (let i = 0; i < rest.length; i++) {
if (first + rest[i] === num) return [first, rest[i]];
}
return answer(rest, num);
};
console.log(answer([1, 2, 3], 4));
console.log(answer([1, 3, 5], 8));
It looks like you are trying to leave .map() and .filter() beforehand, which you can't (without throwing an error). So I suggest a normal for approach for this kind of implementation:
const array1 = [1,2,3];
const easierArray = [1,3,5] //Let's assume number we search what is the sum of 8
const findTwoPartsOfTheNumber = (arr,targetNum) =>{
for(let index = 0; index < arr.length; index++) {
let firstNumber = arr[index];
// console.log('num',num,'index',index);
for(let sub_index = 0; sub_index < arr.length; sub_index++){
const sub_num = arr[sub_index];
// console.log('sub_num',sub_num,'sub_index',sub_index);
if (index != sub_index && (firstNumber + sub_num) === targetNum){
const passableArray = [firstNumber,sub_num]//aka first and second numbers that give the targetNum
return passableArray; //passableArray gives the same output for some reason,it doesn't really matter.
}
}
}
return `there is no such numbers,that give ${targetNum}`;
}
console.log(findTwoPartsOfTheNumber(easierArray,8));
console.log(findTwoPartsOfTheNumber(array1,4));
console.log(findTwoPartsOfTheNumber(array1,10));
I've just grab your code and changed map and filter to for implementation.
There doesn't appear to be any requirement for using specific array functions (map, forEach, filter, etc) in the problem statement you listed, so the code can be greatly simplified by using a while loop and the fact that you know that the second number has to be equal to target - first (since the requirement is first + second == target that means second == target - first). The problem statement also doesn't say what to do if no numbers are found, so you could either return an empty array or some other value, or even throw an error.
const answer = (list, target) => {
while (list.length > 0) { // Loop until the list no longer has any items
let first = list.shift() // Take the first number from the list
let second = target - first // Calculate what the second number should be
if (list.includes(second)) { // Check to see if the second number is in the remaining list
return [first, second] // If it is, we're done -- return them
}
}
return "No valid numbers found" // We made it through the entire list without finding a match
}
console.log(answer([1,2,3], 3))
console.log(answer([1,2,3], 4))
console.log(answer([1,2,3], 7))
You can also add all the values in the array to find the total, and subtract the total by the target to find the value you need to remove from the array. That will then give you an array with values that add up to the total.
let arr1 = [1, 3, 5]
const target = 6
const example = (arr, target) => {
let total = arr.reduce((num1, num2) => {
return num1 + num2
})
total = total - target
const index = arr.indexOf(total)
if (index > -1) {
return arr.filter(item => item !== total)
}
}
console.log(example(arr1, target))
Map and filter are nice functions to have if you know that you need to loop into the whole array. In your case this is not necessary.
So you know you need to find two numbers, let's say X,Y, which belong to an array A and once added will give you the target number T.
Since it's an exercise, I don't want to give you the working code, but here is a few hints:
If you know X, Y must be T - X. So you need to verify that T - X exists in your array.
array.indexOf() give you the position of an element in an array, otherwise -1
If X and Y are the same number, you need to ensure that their index are not the same, otherwise you'll return X twice
Returning the solution should be simple as return [X,Y]
So this can be simplified with a for (let i = 0; i < arr.length; i++) loop and a if statement with a return inside if the solution exist. This way, if a solution is found, the function won't loop further.
After that loop, you return [] because no solution were found.
EDIT:
Since you want a solution with map and filter:
findTwoPartsOfTheNumber = (arr, tNumber) => {
let solution = [];
arr.map((X, indexOfX) => {
const results = arr.filter((Y, indexOfY) => {
const add = Y + X
if (tNumber === add && indexOfX != indexOfY) return true;
else return false;
});
if (results > 0) solution = [X, results[0]];
})
return solution;
}

How to collect all possible contiguous string concatenations from an array of arrays of strings?

I am trying to find an efficient way to collect all possible contiguous string concatenations from an array of arrays of strings, excluding strings with duplicated parts. Example:
var arr = [
["pq","bcd"], ["l", "ffnn", "xyz"], ["hm", "ffnn","ij"], ["ab","def","u","eeff"]
];
function f(a) {
var t = [];
a[a.length-4].forEach(function(i) {
a[a.length-3].forEach(function(j) {
if (j !== i) (
a[a.length-2].forEach(function(k) {
if (k !== j && k !== i) (
a[a.length-1].forEach(function(l) {
if (l !== k && l !== j && l !== i)
(t.push(i+","+j+","+k+","+l));
})
)
})
)
})
});
return t;
};
console.log(f(arr));
where the result will be
["pq, l, hm, ab"],
["pq, l, hm, def"],
//...and so on...
["bcd, xyz, ij, u"],
["bcd, xyz, ij, eeff"]
(Note that while, e.g., ["pq, ffnn, ffnn, ab"] is a possible combination, it is not included in the result because it contains a duplicate).
The problem is that I need to know the length of the array and write multiple nested functions correspondingly. But I need some function which will detect that length automatically, and return the desired result. Maybe it's possible to rewrite the above function using recursion, but I'm not sure if this would be the best approach to such a problem.
If I understand you correctly, given an array of array of strings n, you want a list of all possible arrays m such that
for all i, m[i] is one of n[i]
for all i and j, if i != j, m[i] != m[j]
Well, break in half.
First, consider a function combo that given an array of array of strings, produces an array of the arrays that satisfy (1). How do you write that?
combo on an empty input array produces an array containing only an empty array.
combo on a non-empty input array could work by taking the "head" (the first element of the array), and apply each string from the head in turn and prepending to every array in return value of calling combo on the "tail" (the rest of the input without the head).
Now go through that list and eliminate the entries with duplicates.
Edit: given the Tolstoivian length of some of the other suggestions, I thought I'd post my answer, which uses the Underscore library:
const flatMap = (l, f) => _.flatten(_.map(l, f), true)
const combo = a => a.length?
(v => flatMap(_.head(a), e => v.map(g => [e].concat(g))))
(combo(_.tail(a))):
[[]];
const allUniqueCombos = a => combo(a).filter(n => _.uniq(n).length == n.length)
var arr = [["pq","bcd"], ["l", "ffnn", "xyz"],
["hm", "ffnn","ij"], ["ab","def","u","eeff"]];
console.log(JSON.stringify(allUniqueCombos(arr)))
<script src="http://underscorejs.org/underscore.js"></script>
(This is far from the most efficient use of CPU -- but computers are immensely less expensive than computer programmers.)
You could use four for loops nested in each other.
I managed to get it.
It should work for n-subarrays.
Have a look and let me know if it doesn't work properly.
var arr = [["pq","bcd"], ["l", "ffnn", "xyz"], ["hm", "ffnn","ij"], ["ab","def","u","eeff"]];
var length = arr.length;
var noDuplicate = function (arr, possibleDuplicate) {
var arraySplit = arr.split(",")
for (var i = 0; i < arraySplit.length; i++) {
var arraySplitNoSpace = arraySplit[i].replace(' ', '');
if (arraySplitNoSpace === possibleDuplicate) {
return false;
}
}
return true;
};
var createLoops = function(original, adaptedOriginal, index) { // createLoops(arr, 0, 0);
var temporaryResults = [];
var temporary = adaptedOriginal ? adaptedOriginal : original[0];
for (var i = 0; i < temporary.length; i++) {
for (var j = 0; j < original[index+1].length; j++) {
if (noDuplicate(temporary[i], original[index+1][j])) {
temporaryResults.push(temporary[i] + ", " + original[index+1][j]);
};
};
};
if (index === length-2) {
var results = [];
for (var i = 0; i < temporaryResults.length; i++) {
results.push("[" + temporaryResults[i] + "]");
}
return results;
}
else {
return createLoops(original, temporaryResults, index+1);
};
};
var result = createLoops(arr, 0, 0);
console.log("result: " + result);
console.log("result.length: " + result.length);

Categories

Resources