I have an object. I want to loop through one of it's properties: itself an array of arrays which contain values. For every one of those values, I want to output an array containing a representative value from each of the child arrays, such that every possible combination of values will be output. Where there are more than one value in a child array, a max of 1 value at a time should be allowed. It's at this point that I think it should 'jump on' to the next one (and do the same for all others) but I'm not sure how. The results should look like this:
RABBIT: GREY, FURRY, BOUNCES, CUTE
RABBIT: WHITE, FURRY, BOUNCES, CUTE
RABBIT: RED, FURRY, BOUNCES, CUTE
RABBIT: GREY, FURRY, SCAMPERS, CUTE
RABBIT: WHITE, FURRY, SCAMPERS, CUTE
RABBIT: RED, FURRY, SCAMPERS, CUTE
The array (and it's child arrays) will have unknown lengths, so I've used the for loop. Here is the code so far:
window.onload = function (){
var myObject = {
name: 'RABBIT',
arrayOfValues : [
['GREY','WHITE','RED'],
['FURRY'],
['BOUNCES', 'SCAMPERS'],
['CUTE']
]
};
var results = [];
for (i=0;i<myObject.arrayOfValues.length;i++){
for (j=0; j<myObject.arrayOfValues[i].length;j++){
if(myObject.arrayOfValues[i].length>1) {
var currentProperty = myObject.arrayOfValues[i][j];
myFunc();
}
else {
var currentProperty = myObject.arrayOfValues[i][0];
myFunc();
};
};
};
function myFunc(){
results = results.concat(currentProperty);
if (results.length == myObject.arrayOfValues.length){
var finalResults = myObject.name + ': ' + results
console.log(finalResults);
};
};
};
PS - The form of the data is not set in stone, I just used an object for convenience.
Thanks, Paul
You can make a recursive function call with an increasing index parameter, as well as a string to which you append the new part of the string and return.
var arrayOfArrays = [
['big', 'red'],
['red', 'yellow', 'blue'],
['dog', 'cat']
];
var strings = [];
function eachStep(string_so_far, array_index) {
if (array_index < arrayOfArrays.length) {
for (var i = 0; i < arrayOfArrays[array_index].length; i++) {
var string_for_this_step = string_so_far + arrayOfArrays[array_index][i] + " ";
var string_returned = eachStep(string_for_this_step, array_index+1);
if (string_returned !== "") {
strings.push(string_returned);
}
}
return "";
} else {
return string_so_far;
}
}
eachStep("", 0);
console.log(strings);
Recursion is the native solution here:
// Object as described by question:
var myObject = {
name: 'RABBIT',
arrayOfValues: [
['GREY', 'WHITE', 'RED'],
['FURRY'],
['BOUNCES', 'SCAMPERS'],
['CUTE']
]
};
function permutations(arrays, current_array, idx, results) {
// Init head and results in case this is the first iteration:
idx = idx || 0;
results = results || [];
current_array = current_array || [];
// If there's nothing more to add:
if (arrays.length == idx) {
results.push(current_array);
return;
}
// Otherwise, iterate current level and concat values, while calling next level:
arrays[idx].forEach(function(subArrayItem) {
permutations(arrays, current_array.concat(subArrayItem), idx + 1, results)
});
return results;
}
The function above will return a set of arrays having all combinations, next is a helper function for printing:
// Helper method to print resulting arrays:
function print(obj) {
var separator = "\n"
var prefix = obj.name + ": ";
// Joins the resulting sets with the prefix, and returns printable string:
return prefix + permutations(obj.arrayOfValues).join(separator + prefix)
}
console.log(print(myObject));
Related
I have an array which looks like
var arr = ["a|c", "a|e", "x|z"];
for(var x in arr){
var appsplit = x.split("|");
}
If the first value(ex: a) in the elements matches then it should combine the values
Ex: output
ace
xz
Please advice how this approach can be done.
You are testing everyone's reading comprehension with that riddle.
var pairs = {};
var arr = ["a|c", "a|e", "x|z"];
for(var x in arr)
{
var appsplit = arr[x].split("|");
if(pairs[appsplit[0]] !== "undefined")
{
pairs[appsplit[0]] = pairs[appsplit[0]] + appsplit[1];
}
else
{
pairs[appsplit[0]] = appsplit[1];
}
}
var matches = [];
for(var x in pairs)
{
matches.push(x + pairs[x]);
}
console.log(matches);
We need to map out the arr elements in this object called pairs. The first value in your split would be the key and the second value is appended (or assigned if it's the first match to the key)
You made an error of splitting x, but you are only splitting the index of the element, not the actual value of the element. arr[x] is the actual value, where x specifies the index in the array.
After we've gone through your arr, we can now merge the key with the values. Your output is contained in matches where the key in each pair is prepended to the value of the key's pair.
Some simple code that would to the trick here.
var arr = ["a|c", "a|e", "x|z", "c|b", "z|e", "c|a"];
var resultObj = {};
arr.forEach(function(element, index){
var array = element.split('|');
if(array.length!==2){
console.log("skipping, invalid input data", element);
} else {
var firstLetter = array[0];
var secondLetter = array[1];
if(resultObj[firstLetter]){
resultObj[firstLetter].push(secondLetter);
} else {
resultObj[firstLetter]=[secondLetter];
}
}
});
Object.keys(resultObj).forEach(function(key){
console.log(key + "," + resultObj[key]);
});
You can use .reduce(), Set to not accumulate duplicate values, .some() to check if previous array contains value in current array, .map(), Array.from() and .join() to convert array to string
var arr = ["a|c", "a|e", "x|z"];
var res = arr.reduce(function(a, b) {
var curr = b.split("|");
var set = new Set;
for (let prop of curr) set.add(prop);
if (!a.length) {
a.push(set)
} else {
for (prop of a) {
if (curr.some(function(el) {
return prop.has(el)
})) {
for (el of curr) {
prop.add(el)
}
} else {
for (let prop of curr) set.add(prop);
a.push(set)
}
}
}
return a
}, []).map(function(m) {
return Array.from([...m], function(el) {
return el
}).join("")
});
console.log(res);
I feel like this can be done more elegantly, but I didn't have time to streamline it. :) The below code will do what you want, though:
var aStartArray = **ARRAY_VALUE_HERE**;
var aSplitResultStrings = [];
// step through each element in the array
for (var i = 0, iSALength = aStartArray.length; i < iSALength; i++) {
// split the values for the current array element
var aSplitVal = aStartArray[i].split("|");
var bStringDoesNotExist = true;
// loop through the "result strings" array
for (var j = 0, iSRSLength = aSplitResultStrings.length; j < iSRSLength; j++) {
// if the first letter from the array element = the first letter of the current "result string" . . .
if (aSplitResultStrings[j].charAt(0) === aSplitVal[0]) {
// append the second letter of the array value to the current result string
aSplitResultStrings[j] = aSplitResultStrings[j] + aSplitVal[1];
// indicate that a match has been found and exit the "result string" loop
bStringDoesNotExist = false;
break;
}
}
// if there are no result strings that start with the first letter of the array value . . .
if (bStringDoesNotExist) {
// concatenate the two values in the current array value and add them as a new "result string"
aSplitResultStrings.push(aSplitVal[0] + aSplitVal[1]);
}
}
Using these arrays, the results are:
aStartArray = ["a|c", "a|e", "x|z"] //results in:
aSplitResultStrings = ["ace", "xz"]
aStartArray = ["a|b", "a|c", "a|d", "a|e", "x|y", "x|z"] //results in:
aSplitResultStrings = ["abcde", "xyz"]
aStartArray = ["a|b", "d|e", "d|f", "x|y", "g|h", "g|i", "m|n", "g|j", "a|c", "x|z"] //results in:
aSplitResultStrings = ["abc", "def", "xyz", "ghij", "mn"]
As I said, this could be more elegant (for example, you could probably use Map to make iterating through the "result strings" easier), but this makes the steps pretty clear and should get you going down the right path towards a final solution.
Let's says I've an array['Alex', 'Sam', 'Robert']
I'd like to combine them something like:
Take first array[0] and append with array[2] which will be AlexRobert
first letter of array[0] which is A and append with array[2] that is Robert which will be ARobert
Take array[0] which is Alex and append with first letter of array[2] that is R which will be AlexR
Take first array[0] append with first letter of array[1] along with array[2] which will become AlexSRobert.
Basically the whole idea is when someone enter first name, middle name & last name I should be able to make combination and guess email ids. For example- Juan F. Nathaniel the array form will be like ['Juan', 'F', 'Nathaniel']
I want the combination of first, middle and last name like jaunn, jnathaniel, jaunfnathaniel
I'm beginner and here is what I've written:
var nameCombination = function(name){
var counting = name.split(" ");
for (var i=0; i<counting.length; i++){
console.log(counting[i] + counting[i+1]);
console.log(counting[i].split("",1) + counting[i+1]);
}
}
nameCombination('Alex Sam Robert');
I'm assuming you needed a function to do this? Here is a function to handle grabbing pieces of each index of the array. I'll leave it up to you to figure out what type of data you need...
var test = function() {
var array = ['Alex', 'Sam', 'Robert'];
var conditions = [{
index: 0,
length: array[0].length
},
{
index: 1,
length: 1
},
{
index: 2,
length: array[2].length
}]
alert(combine(array, conditions));
}
var combine = function(array, conditions) {
var output = "";
for(index in conditions) {
var condition = conditions[index];
var index = condition['index'];
var length = condition['length'];
output += array[index].substring(0, length);
}
return output;
}
test();
You could use an iterative and recursive approach for variable length of parts an their length.
function combine(array) {
function c(part, index) {
array[index].forEach(function (a) {
var p = part.concat(a);
if (p.length === array.length) {
r.push(p.join(''));
return;
}
c(p, index + 1);
});
}
var r = [];
c([], 0);
return r;
}
var input= ['Johann', 'Sebastian', 'Bach'],
array = input.map(function (a) { return ['', a[0], a]; });
result = combine(array);
console.log(result);
This problem can be solved using recursive approach.
var combinations = function(names, i, n){
if(i == n){
return [];
}
last_names = combinations(names, i + 1, n);
name_combinations = last_names.map(function(last_name){
return [
last_name,
names[i] + last_name,
names[i] + last_name[0],
names[i][0] + last_name,
names[i][0] + last_name[0]
]
});
name_combinations = [].concat.apply([], name_combinations);
name_combinations.push(names[i]);
return name_combinations;
};
var nameCombinations = function(name){
var name_array = name.split(' ');
return Array.from(new Set(combinations(name_array, 0, name_array.length)));
};
nameCombinations('first last');
above function can generate all the desired combinations for a given name.
for example: nameCombinations('first last') will return ["last", "firstlast", "firstl", "flast", "fl", "first"].
Ok without writing out every combination I will do the first few to give you the idea:
assuming
array[0] is the person's first name
array[1] is the person's middle name
array[2] is the person's last name
Firstname+Lastname:
var output = array[0] + array [2];
Firstname+Middlename:
var output1 = array[0] + array[1];
then then you could display the output using innerHTML:
Javascript:
document.getElementById("output").innerHTML = output + '<br>' + output1;
HTML:
<div id="output"></div>
Keep in mind you would need to keep doing that for the rest of the combinations.
Now for the combinations where you need to get the first letter of the variable you need to use charAt which I found from this stack overflow answer.
You would do the same thing as before, except instead you need to use charAt and do something like so:
Firstname+FirstLetterOfLastName:
var output2 = array[0] + array[2].charAt(0);
And you can output it using the same method as before.
If your still confused leave a comment and I will try and answer your questions.
Given an array of words, write a function that returns an array of the words that occur an even number of times.
function even(["hello", "hi", "hello", "elephant", "hi"]);
That output should be:
["hello", "hi"]
This has been a toy problem I have been struggling with recently. I have solved similar problems counting and returning the number of occurrences of elements in an array but am having trouble taking that logic and applying it to this problem.
This is what I have tried so far, but have hit a wall when trying to output just the even occurrences:
function even(collection) {
var results = [];
for(var i = 0; i < collection.length; i++){
var value = collection[i];
if(results[value]){
results[value] = results[value] + 1;
}else{
results[value] = 1;
}
}
return results;
}
You can use reduce to get an actual count of the words, then simply return an array of the ones that have an even count:
function even(wordsArr) {
//Object of words and counts
var wordCounts = wordsArr.reduce(function(counts, word) {
if (!counts.hasOwnProperty(word)) {
counts[word] = 0;
}
counts[word]++;
return counts;
}, {});
//Now filter that out and return
return Object.keys(wordCounts).filter(function(word) {
return wordCounts[word] % 2 === 0
});
}
even(["hello", "hi", "hello", "elephant", "hi"]); //["hello", "hi"]
var arr = ["hello", "hi", "hello", "elephant", "hi"];
function onlyEvens( arr )
{
var countObj = {};
for( var i = 0; i < arr.length; i++ )
{
var item = arr[i];
if( countObj[ item ] !== undefined )
countObj[item]++;
else
countObj[item] = 1;
}//for()
var filteredArray = [];
for(var key in countObj )
{
if( countObj[key] % 2 == 0 )
filteredArray.push( key );
}
return filteredArray;
}//onlyEvens()
console.log( onlyEvens( arr ) );
Issues in your code:
you use collection instead of words
you cannot access array the associative way. You must declare it as object:
results[value]
you return result variable, but it is undeclared.
return result;
results only contains the occurrences of every word. There miss the code that calculates if the occurrences of a word are odd or even.
fixed code:
function even(words) { // <<< in your code was collection
var results = {};
for(var i = 0; i < words.length; i++){
var value = words[i];
if(results[value]){
results[value] = results[value] + 1;
}else{
results[value] = 1;
}
}
var ret = [];
for(var word in results)
if(results[word]%2 !== 0)
rest.push(word);
return ret;
}
function even(list) {
var d = list.reduce(function(d, w) { d[w] = !d[w]; return d; }, {});
return Object.keys(d).filter(function(w) { return !d[w]; });
}
console.log(even(["hello", "hi", "hello", "elephant", "hi"]));
console.log(even(["hello", "yo", "yo", "hi", "hello", "yo", "elephant", "hi"]));
Explanation: Use the array .reduce() method to create an object (d) with a property for each word (w) with a boolean value indicating whether the word has an odd number of occurrences. Then .filter() the keys to get all the ones that are not odd.
If you previously sort the array you can filter it as required in just a code line like this :
var even = (str) => str.sort().filter((element, index, arr) => index+1 === arr.lastIndexOf(element));
console.log(even(["hello", "hello", "hi", "elephant", "hi", "hi"])); //[ 'hello', 'hi' ]
I have string like the following:
11222233344444445666
What I would like to do is output the number followed the times it was displayed:
112433475163
Question is, I want this to be efficient. I can store this in an object as the following:
1: { id: 1, displayed: 2},
2: { id: 2, displayed: 1},
3: { id: 3, displayed: 2},
etc.
I can access this object and increment displayed.
My issues is, there is no guarantee in the order. I would like to store the keys in the order they are in the string. How do I accomplish the importance of the order in the object?
This is a proposal for run length coding with an array which holds infomation about one charcter and the count of it:
{
"char": "1",
"count": 2
},
var string = "11222233344444445666",
array = function () {
var r = [], o = {};
string.split('').forEach(function (a, i, aa) {
if (a !== aa[i - 1]) {
o[a] = { char: a, count: 0 };
r.push(o[a]);
}
o[a].count++;
});
return r;
}(string);
document.write('<pre>' + JSON.stringify(array, 0, 4) + '</pre>');
Quick solution with for loop:
var str = "7771122229933344444445666",
obj = {},
len = str.length,
val = null,
count_str = "",
key = "";
for (var i = 0; i < len; i++) {
val = str[i], key = 'k' + val;
if (!obj[key]) {
obj[key] = {'id': val, 'displayed': 1};
} else {
obj[key].displayed++;
}
}
for (var p in obj) {
count_str += obj[p]['id'] + obj[p]['displayed'];
}
console.log(count_str); // "7312249233475163"
because you have such a small set of distinct numbers, I seen no reason why you can't use a array (yeah it's not super ideal memorywise if you skip values and it becomes sparse, but for such a small subset it won't affect you enough to worry of it). Then you can use (number-1) as the index and increment that number as needed.
var counts = [];
var str = "11222233344444445666";
for(var i in str){
var index = parseInt(str[i])-1
counts[index] = (counts[index]||0)+1;
}
for(var i in counts){
var which = 1+parseInt(i);
var count = counts[i];
console.log("# of " + which +"'s: "+count);
}
https://jsfiddle.net/ga0fqpqn/
note: You shouldn't need the parseInt(i)... just +i should work but I think jsfiddle has a bug with it about it defaulting i to handle like a string.
You could store an additional array with the order of the numbers, which you only append to if the object doesn't yet contain the given number. Then once you're done counting, iterate through that array and output the number and the count from the lookup dictionary.
var chars = "1234576123452345".split("");
var order = [];
var hash = {};
chars.forEach(function(char) {
if (!hash[char]) {
hash[char] = 1;
order.push(char);
} else {
hash[char]++;
}
});
console.log(order.map(function(char) {
return char + hash[char];
}).join(""));
// "12233343537161"
I've fried my brain looking at this for too long. With the following little JavaScript:
var words = 'one two three four one two three';
wordArray = words.split(' ');
var newArray = [];
var words = {};
words.word;
words.count;
$.each(wordArray, function (ix, val) {
if ($.inArray(wordArray[ix], newArray) > -1) {
newArray[wordArray[ix]].count++;
}
else {
console.log('that wasnt in the array');
newArray.push(wordArray[ix]);
newArray[wordArray[ix]].count = 1;
}
});
I get the error cannot set property 'count' of undefined. Why can't I dynamically add the property and have anything work fine?
newArray should be an object whose properties are the words and values are the count:
var newArray = {};
$.each(wordArray, function (ix, val) {
if (newArray[val]) {
newArray[val]++;
}
else {
console.log('that wasnt in the array');
newArray[val] = 1;
}
});
seems like you are trying to invent a property count on the Array. I think you just want length unless newArray is something other then the native Array object. if it is just a ["word", "another"] then you don't need to do anything to increment the length property it is done internally
UPDATE
ok then defninately the problem is the count property newArray.length will do what you are trying to do with .count
UPDATE 2
ok so it looks like you want to do this:
var words = 'one two three four one two three';
wordArray = words.split(' ');
var newArray = [];
var words = {};
$.each(wordArray, function (ix, word ) {
if ($.inArray(word , newArray) > -1) {
words[word]++;
}
else {
console.log('that wasnt in the array');
words[word] = 1;
}
});
this would give you an object where the word is the key and the value is the count like:
{ "one": 2, "four": 1} //etc