Javascript/Jquery array searching - javascript

I'm working on an application that uses nested arrays in Javascript. I have an array like this:
var a = ["Once upon a time there was a man."];
a[1] = ["He was tall."];
a[1][1] = ["He often bumped his head."];
a[2] = ["He was short."];
a[2][1] = ["He couldn't reach high shelves."]
Is there an easy way to figure out what level of an array a value is at? I'd like to be able to input "He often bumped his head" and have it return "a[1][1]" (or if I input "He was short" I'd get back "a[2]"). The arrays will be of indeterminate and fluctuating size.
Thanks in advance for any help with this. I'm new to javascript and jquery, so any explanation of your solution will be highly appreciated. Thanks again.

For a two level array, you could do it like this:
function findMultiArray(array, str) {
var innerArray;
for (var i = 0; i < array.length; i++) {
innerArray = array[i];
for (var j = 0; j < innerArray.length; j++) {
if (innerArray[j] == str) {
return([i, j]);
}
}
}
return null;
}
Note, I had it just return an actual array with the two indexes since that's a little easier to use if you need the data in data form.
Unlike in your request, it will never return the equivalent of a[1] because a[1] is an array, not a string so the match is with a[1][0], not with a[1]. In that case, it would return [1,0].
If you want the return value in a different form, there's just one line of code where is sees the match and i and j are the indexes that matched.
If the level of arrays can be arbitrarily deep with different depths in different places, then that would be a little more involved and would require interrogating the type of items to see if they contained nested arrays or just strings and would require some sort of stack to maintain the current position. It would be easiest to do with recursion.

Some recursion should do the trick:
Array.prototype.recursiveIndexOf = function(item, start) {
var i = this.indexOf(item);
var r, c;
start = start || [];
if(i > -1) {
return start.concat([i]);
}
for(i = 0; i < this.length; i++) {
c = this[i];
if(Object.prototype.toString.call(c) === '[object Array]') {
r = c.recursiveIndexOf(item, start.concat(i));
if(r !== null) {
return r;
}
}
}
return null;
};
Here's a demo.

Here is a much simpler answer. ;)
function array_search(ob,str)
{
for(var i=0;i<ob.length;++i)
{
if(typeof(ob[i])=='object'&&(ob[i] instanceof Array))
{
var foo=array_search(ob[i],str);
if(foo!=null)
return i+'->'+foo;
}else
if(typeof(ob[i])=='string'&&ob[i]==str)
{
return i;
}
}
return null;
}
var a = ["Once upon a time there was a man."];
a[1] = ["He was tall."];
a[1][1] = ["He often bumped his head."];
a[2] = ["He was short."];
a[2][1] = ["He couldn't reach high shelves."]
alert(array_search(a,'He was short.'));

Related

Finding an array of arrays... inside another array

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

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);

JS Return array based on the comparison of two arrays with nested elements

Using Angular and trying to return result based on the comparison of two arrays. Here is what I have:
$scope.fbFriends = [{"id":1234,"name":'bob'},
{"id":4567,"name":'john'},
{"id":8910,"name":'totoro'}];
$scope.appFriends = [{"id":1,"name":'bob',"fb_id":1234},
{"id":2,"name":'john',"fb_id":4567}];
I would like to filter the friends that exist in both and return only the ones from fbFriends that don't exist in appFriends.
This is what I did but it doesn't work it returns way too many times the same.
$scope.not_friends = [];
$scope.filtered = [];
for (var i=0; i < $scope.fbFriends.length; i++) {
for (var j=0; j < $scope.appFriends.length; j++) {
if ($scope.fbFriends[i].id !== $scope.appFriends[j].fb_id) {
$scope.not_friends = $scope.fbFriends[i];
$scope.filtered.push($scope.not_friends);
}
}
};
console.log($scope.filtered);
What is wrong in this approach?
Bonus, could I integrate that in a filter and use it in a ng-repeat of fbFriends ?
Thanks!!
The problem is the if block: you have to check for equality and then - since you are searching for friends that are NOT contained in the fbFriends - remove the object.
I've rewritten your code and it seems to work properly.
$scope.fbFriends = [{"id":1234,"name":'bob'},
{"id":4567,"name":'john'},
{"id":8910,"name":'totoro'}];
$scope.appFriends = [{"id":1,"name":'bob',"fb_id":1234},
{"id":2,"name":'john',"fb_id":4567}];
for (var i=0; i < $scope.appFriends.length; i++) {
// we parse the 'reference array'
for (var j=0; j < $scope.fbFriends.length; j++) {
// we check every value inside the 'container array'
if ($scope.fbFriends[j].id == $scope.appFriends[i].fb_id) {
// the element is alredy inside the appFriends
$scope.fbFriends.splice(j,1);
}
}
}
console.log($scope.fbFriends);
For further reference see an algorithm book (Mine suggestion is Introduction to Algorithms by Thomas H. Cormen, but anyone will do.)
Here's a generic set difference operation
difference = function(a, b, eq) {
return a.filter(function(x) {
return b.every(function(y) {
return !eq(x, y)
});
});
}
where a, b are arrays and eq - an equality function.
And this is how to apply it to your problem:
fbFriends = [{"id":1234,"name":'bob'},
{"id":4567,"name":'john'},
{"id":8910,"name":'totoro'}];
appFriends = [{"id":1,"name":'bob',"fb_id":1234},
{"id":2,"name":'john',"fb_id":4567}];
difference = function(a, b, eq) {
return a.filter(function(x) {
return b.every(function(y) {
return !eq(x, y)
});
});
}
onlyFb = difference(fbFriends, appFriends, function(f, a) {
return f.id === a.fb_id
});
document.write(JSON.stringify(onlyFb))

Check if the values of one array are in another

I need to check if array number 2 contains all of the values in array number 1. I am not aware of any method that does this so I developed one that works, I think. Is there a better way to do this, is this a good solution?
var contains = function(a1, a2){
var cCount = 0;
for (var i=0; i<a1.length; i++){
for (var j=0; j<a2.length; j++){
if (a1[i] == a2[j]){
cCount++;
}}}
if (cCount == a1.length){
return true;
}
};
You could check sizes before starting. return false when one is not present instead using a counter. and return true if it reach the end. And use indexof instead looping through a2 every time.
var contains = function(a1, a2){
if (a1.length>a2.length) return false;
for (var i=0; i<a1.length; i++){
if (a2.indexOf(a1[i])<0) return false;
}
return true;
}
just a bit simplified code:
function contains(array1, array2){
var found = false;
for (i in array1) {
for (j in array2) {
if (array1[i] == array2[j]) {
found = true;
}
}
if (!found) return false;
}
return true;
}
another solution I don't really like it but it's shorter...
function contains (arr1, arr2) {
var specialChar = "|"; // Use any char or a sequence that won't exist in values.
var str = specialChar + arr2.join(specialChar) + specialChar;
for (i in arr1) if (str.indexOf(specialChar + arr1[i] + specialChar) == -1) return false;
return true;
}
Your solution is O(n * n) i.e. order n-squared.
You could sort the arrays first and then sequentially check the elements in the sorted arrays for a match. This will give you an O(n log n) solution. Also you can short circuit the check by ensuring that the size of array2 <= the size of array1.
Evidently this only matters if your arrays are sufficiently big.
You can do this in O(n) if you have a 3rd object that you use to keep track of the items that have been seen already. This assumes that the lookup in seen is O(1) (which presumably it is - What's the big O for JavaScript's array when used as a hash?)
var seen = {};
arr2.forEach(function(el) {
seen[el] = true;
});
var allContained = true;
arr1.forEach(function(el) {
if ( allContained && !seen[el] ) {
allContained = false;
}
});
return allContained;
I'd personally use the Array.every() method (though this is, of course, dependent upon a browser that implements this method) in conjunction with Array.indexOf(), which would result in something akin to the following:
var contains = function(needle, haystack){
return needle.every(function(a){
return haystack.indexOf(a) > -1;
});
};
Combining that with the approach you've already produced (testing for browser-support):
var contains = function(needle, haystack){
if ([].every){
return needle.every(function(a){
return haystack.indexOf(a) > -1;
});
}
else {
var result = true;
for (var i = 0, len = needle.length; i < len; i++){
if (haystack.indexOf(needle[i]) === -1) {
return false;
}
}
return result;
}
}
var a1 = [1,2,3],
a2 = [1,2,3,4];
console.log(contains(a1, a2));
JS Fiddle demo.
Note that the else code isn't optimised, it's simply there to demonstrate the code. Having said that, there is a shim for Array.every() at the MDN page (in the references, below) that might make things easier.
References:
Array.prototype.every().
Array.prototype.indexOf().

Shuffle an array as many as possible

I have an array like this
[0,2,3]
The possible shuffling of this array are
[0,2,3], [2,3,0], [3,0,2], [3,2,0], [0,3,2], [2,0,3]
How can I get these combinations? The only idea I have in mind currently is
n = maximum num of possible combinations, coms = []
while( coms.length <= n )
temp = shuffle( original the array );
if temp is there in coms
return
else
coms.push(temp);
But I do not think this is efficient, as here we are blindly depending on uniform distribution of shuffle method.
Is there alternative findings for this problem?
The first thing to note is that the number of permutations increases very fast with regard to the number of elements (13 elements = 6 bilion permutations), so any kind of algorithm that generates them will deteriorate in performance for a large enough input array.
The second thing to note is that since the number of permutations is very large, storing them in memory is expensive, so you're way better off using a generator for your permutations and doing stuff with them as they are generated.
The third thing to note is that recursive algorithms bring a large overhead, so even if you find a recursive solution, you should strive to get a non-recursive one. Obtaining a non-recursive solution if a recursive one exists is always possible, but it may increase the complexity of the code.
I have written a non recursive implementation for you, based on the Steinhaus–Johnson–Trotter algorithm (http://en.wikipedia.org/wiki/Steinhaus%E2%80%93Johnson%E2%80%93Trotter_algorithm)
function swap(arr, a,b){
var temp = arr[a];
arr[a]=arr[b];
arr[b]=temp;
}
function factorial(n) {
var val = 1;
for (var i=1; i<n; i++) {
val *= i;
}
return val;
}
function permute(perm, func){
var total = factorial(perm.length);
for (var j=0, i=0, inc=1; j<total; j++, inc*=-1, i+=inc) {
for (; i<perm.length-1 && i>=0; i+=inc) {
func.call(perm);
swap (perm, i, i+1);
}
func.call(perm);
if (inc === 1) {
swap(perm, 0,1);
} else {
swap(perm, perm.length-1, perm.length-2);
}
}
}
console.clear();
count = 0;
permute([1,2,3,4,5,6], function(){console.log(this); count++;});
console.log('There have been ' + count + ' permutations');
http://jsbin.com/eXefawe/2/edit
Try a recursive approach. Here's a hint: every permutation of [0,2,3] is either
[0] plus a permutation of [2,3] or
[2] plus a permutation of [0,3] or
[3] plus a permutation of [0,2]
As zodiac mentioned the best solution to this problem is a recursive one:
var permute = (function () {
return permute;
function permute(list) {
return list.length ?
list.reduce(permutate, []) :
[[]];
}
function permutate(permutations, item, index, list) {
return permutations.concat(permute(
list.slice(0, index).concat(
list.slice(index + 1)))
.map(concat, [item]));
}
function concat(list) {
return this.concat(list);
}
}());
alert(JSON.stringify(permute([1,2,3])));
Hope that helps.
All permutations of a set can be found by selecting an element in the set and recursively permuting (rearranging) the remaining elements. Backtracking approach can be used for finding the solution.
Algorithm steps (source):
Pseudocode (source):
permute(i)
if i == N output A[N]
else
for j = i to N do
swap(A[i], A[j])
permute(i+1)
swap(A[i], A[j])
Javascript implementation (jsFiddle):
Array.prototype.clone = function () {
return this.slice(0);
};
var input = [1, 2, 3, 4];
var output = [];
function permute(i) {
if (i == input.length)
output.push(input.clone());
else {
for (var j = i; j < input.length; j++) {
swap(i, j);
permute(i + 1);
swap(i, j); // backtrack
}
}
};
function swap(i, j) {
var temp = input[i];
input[i] = input[j];
input[j] = temp;
}
permute(0);
console.log(output);
For an array of length n, we can precompute the number of possible permutations. It's n! (n factorial)
function factorial(n){ return n<=0?1:n*factorial(n-1);}
//There are better ways, but just for illustration's sake
And, we can create a function which maps an integer p between 0...n!-1 to a distinct permutation.
function map(p,orgArr){
var tempArr=orgArr.slice(); //Create a copy
var l=orgArr.length;
var permArr=[];
var pick;
do{
pick=p%l; //mod operator
permArr.push(tempArr.splice(pick,1)[0]); //Remove item number pick from the old array and onto the new
p=(p-pick)/l;
l--;
}while(l>=1)
return permArr;
}
At this point, all you need to do is create an array ordering=[0,1,2,3,...,factorial(n)-1] and shuffle that. Then, you can loop for(var i=0;i<=ordering.length;i++) doSomething(map(ordering[i],YourArray));
That just leaves the question of how to shuffle the ordering array. I believe that's well documented and outside the scope of your question since the answer depends on your application (i.e. is pseudo random good enough, or do you need some cryptographic strength, speed desired, etc...). See How to randomize (shuffle) a JavaScript array? and many others.
Or, if the number of permutations is so large that you don't want to create this huge ordering array, you just have to distinctly pick values of i for the above loop between 0-n!-1. If just uniformity is needed, rather than randomness, one easy way would be to use primitive roots: http://en.wikipedia.org/wiki/Primitive_root_modulo_n
you don't need to recurse. The demo should make the pattern pretty clear: http://jsfiddle.net/BGYk4/
function shuffle(arr) {
var output = [];
var n = arr.length;
var ways = [];
for(var i = 0, j = 1; i < n; ways.push(j *= ++i));
var totalWays = ways.pop();
for(var i = 0; i < totalWays; i++) {
var copy = arr.slice();
output[i] = [];
for(var k = ways.length - 1; k >= 0; k--) {
var c = Math.floor(i/ways[k]) % (k + 2);
output[i].push(copy.splice(c,1)[0]);
}
output[i].push(copy[0]);
}
return output;
}
this should output all possible "shuffles"
console.log(shuffle(['a', 'b', 'c', 'd', 'e']));
this one's cuter (but the output from the other example is much clearer): http://jsfiddle.net/wCnLf/
function shuffle(list) {
var shufflings = [];
while(true) {
var clone = list.slice();
var shuffling = [];
var period = 1;
while(clone.length) {
var index = Math.floor(shufflings.length / period) % clone.length;
period *= clone.length;
shuffling.push(clone.splice(index,1)[0]);
}
shufflings.push(shuffling);
if(shufflings.length == period) return shufflings;
}
}
and of course it still outputs all possible "shuffles"
console.log(shuffle(['a', 'b', 'c', 'd', 'e']));
tibos example above was exactly what I was looking for but I had some trouble running it, I made another solution as an npm module:
var generator = new Permutation([1, 2, 3]);
while (generator.hasNext()) {
snippet.log(generator.next());
}
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
<script src="http://rawgit.com/bcard/iterative-permutation/master/iterative-permutation.js"></script>
https://www.npmjs.com/package/iterative-permutation
https://github.com/bcard/iterative-permutation

Categories

Resources