This question already has answers here:
Javascript equivalent of Python's zip function
(24 answers)
Closed 2 years ago.
I've an Object containing data that looks like this,
obj = {
Q1:['val1','val2'],
Q2:['val3','val4','val5'],
Q3:['val8']
}
I was trying to loop over keys and get and first element in each key concate each element in each array, and join them together using , (my object has more keys that this ofc)
So the output should be like
val1,val3,val8
val2,val4,
,val5,
I tried to loop over keys and getting each value but i think i'm missing something in my loop, as i can't change the key if it found element in each object
These are my trials below.
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
for (let i = 0; i < obj[key].length; i++) {
console.log(obj[key][i])//This is always looping on the same key but different element
}
}
}
while i want it to be something close to
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
console.log(obj[key][i])
}
}
My solution uses a deep copy of the object and then a recursive array manipulation via shift():
obj = {
Q1:['val1','val2'],
Q2:['val3','val4','val5'],
Q3:['val8']
}
var resObj = [];
function printByIndex(obj){
var newObj = JSON.parse(JSON.stringify(obj));
printByIndexHelper(newObj, 0);
}
function printByIndexHelper(obj, i){
var flag = false;
resObj[i] = [];
Object.keys(obj).forEach(function(key){
if(obj[key].length > 0){
resObj[i].push(obj[key].shift());
if(obj[key].length > 0){
flag = true;
}
}else{
resObj[i].push(null);
}
});
if(flag){
printByIndexHelper(obj, i+1);
}
}
printByIndex(obj);
console.log(resObj);
Maps over arrays and joins string. Math.max to get full length to iterate over. Uses flatMap to filter out unequal length array values.
obj = {
Q1:['val1','val2'],
Q2:['val3','val4','val5'],
Q3:['val8']
}
const o = Object.values(obj)
const len = Math.max(...o.map(({length})=>length))
console.log(
Array(len).fill().map((x,i)=>o.flatMap(q=>q[i]||[]).join(','))
)
check this same output as u require
obj = {
Q1:['val1','val2'],
Q2:['val3','val4','val5'],
Q3:['val8']
}
var keys = Object.keys(obj);
var stack = [];
var maxLength = getMaxLength(obj);
for(var k = 0; k < 3;k ++) {
var arr = [];
for(var i = 0; i<keys.length;i++) {
const key = keys[i];
const elements = obj[key];
if(k < elements.length) {
arr.push(elements[k]);
}
}
console.log(arr+"\n");
//stack.push(arr);
}
function getMaxLength(jsonObj) {
var max;
var jsonKeys = Object.keys(jsonObj);
for(var key of jsonKeys) {
const arr = jsonObj[key];
if (max == null || arr.length > max) {
max = arr.length;
}
}
return max;
}
Related
I am trying to solve a freeCodeCamp exercise and have gotten stuck. The goal of the exercise is this: Make a function that looks through an array of objects (first argument) and returns an array of all objects that have matching property and value pairs (second argument). Each property and value pair of the source object has to be present in the object from the collection if it is to be included in the returned array.
So what I did, was to make an array of the key pairs of the collection, and another array with the key pairs of the source. The I nested for-loops in order to find matching keys, and if those keys are found, then compare the properties.
But somehow, my code returns no matches.
var collection = [{
first: "Romeo",
last: "Montague"
}, {
first: "Mercutio",
last: null
}, {
first: "Tybalt",
last: "Capulet"
}];
var source = {
last: "Capulet"
};
var collectionKeys = [];
for (var i = 0; i < collection.length; i++) {
collectionKeys.push(Object.keys(collection[i]));
}
var sourceKeys = Object.keys(source);
//for every key pair
for (var t = 0; t < collectionKeys.length; t++) {
//for every key in key pair
for (var x = 0; x < collectionKeys[t].length; x++) {
//for every key in search
for (var y = 0; y < sourceKeys.length; y++) {
//see if a key matches
if (sourceKeys[y] == collectionKeys[t][x]) {
//see if the value matches
if (collection[collectionKeys[t][x]] == source[sourceKeys[y]]) {
console.log(collection[t]);
} else {
console.log("value not found");
}
} else {
console.log("key not found");
}
}
}
}
Can anybody point out what I'm doing wrong?
I've also created a JSfiddle if you want to tinker.
I was also stuck on this for a good hour, when I stumbled upon a couple resources to assist.
I found that rather than the mess of nested for loops, I could use the built in looping methods to greatly simplify my code.
here is where I found my explanation:
https://github.com/Rafase282/My-FreeCodeCamp-Code/wiki/Bonfire-Where-art-thou
function where(collection, source) {
var arr = [];
var keys = Object.keys(source);
// Filter array and remove the ones that do not have the keys from source.
arr = collection.filter(function(obj) {
//Use the Array method every() instead of a for loop to check for every key from source.
return keys.every(function(key) {
// Check if the object has the property and the same value.
return obj.hasOwnProperty(key) && obj[key] === source[key];
});
});
return arr;
}
be more explicit in your declarations - helps to read the code easier:
var sourceKeys = Object.keys(source),
i = 0,
j = 0,
collectionLength = collection.length,
sourceKeysLength = sourceKeys.length;
while (i < collectionLength) {
j = 0;
while (j < sourceKeysLength) {
if (sourceKeys[j] in collection[i] && source[sourceKeys[j]] === collection[i][sourceKeys[j]]) {
console.log('found one!');
}
j++;
}
i++;
}
https://jsfiddle.net/fullcrimp/1cyy8z64/
Some insight here with clear understanding and less loops.
some new javascript function like some, filter, map are really handy to make code tidier as well.
function whatIsInAName(collection, source) {
// What's in a name?
var arr = [];
// Only change code below this line
collection.some(function(obj){
var sk = Object.keys(source); //keys of source object
var sv = Object.values(source); //values of source object
var temp = 0;
for(i=0;i<sk.length;i++){ // run until the number of source properties length is reached.
if(obj.hasOwnProperty(sk[i]) && obj[sk[i]] === sv[i]){ // if it has the same properties and value as parent object from collection
temp++; //temp value is increased to track if it has matched all the properties in an object
}
}
if(sk.length === temp){ //if the number of iteration has matched the temp value
arr.push(obj);
temp = 0; // make temp zero so as to count for the another object from collection
}
})
// Only change code above this line
return arr;
}
var collection = [{
first: "Romeo",
last: "Montague"
}, {
first: "Mercutio",
last: null
}, {
first: "Tybalt",
last: "Capulet"
}];
var source = {
last: "Capulet"
};
var collectionKeys = [];
for (var i = 0; i < collection.length; i++) {
collectionKeys.push(Object.keys(collection[i]));
}
var sourceKeys = Object.keys(source);
//for every key pair
for (var t = 0; t < collectionKeys.length; t++) {
//for every key in key pair
for (var x = 0; x < collectionKeys[t].length; x++) {
//for every key in search
for (var y = 0; y < sourceKeys.length; y++) {
//see if a key matches
if (sourceKeys[y] == collectionKeys[t][x]) {
if (collection[t][collectionKeys[t][x]] == source[sourceKeys[y]]) {
alert(collection[t].first+ " "+collection[t].last);
} else {
console.log("value not found");
}
} else {
console.log("key not found");
}
}
}
}
Change collection[collectionKeys[t][x]] to collection[t][collectionKeys[t][x]]..collection[collectionKeys[t][x]] gives undefined in console.
This is what I came to on the same problem.
function whereAreYou(collection, source) {
// What's in a name?
// Only change code below this line
var arr = [];
var validObject;
// check each object
for (var each_object in collection ){
validObject = true;
for (var key in source ){
if ( collection[each_object].hasOwnProperty(key)){
if ( collection[each_object][key] != source[key]){
// if no valid key
validObject = false;
}
} else {
// if no valid value
validObject = false;
}
}
// otherwise, give it a green light
if(validObject){
arr.push(collection[each_object]);
}
}
return arr;
}
function whatIsInAName(collection, source) {
const keyCount = Object.keys(source).length;
return collection.filter((item) => {
return Object.entries(item).reduce((acc, [key, value], _, arr) => {
if (keyCount > arr.length) {
acc = false;
} else if (keyCount === arr.length && !source[key]) {
acc = false;
} else if (source[key] && source[key] !== value) {
acc = false;
}
return acc;
}, true)
})
}
I have a JavaScript array:
var data = [{abc: "vf",def: [{ a:"1", b:"3"},{a:"2",b:"4"}]}];
I want it to convert to:
[{"abc":"vf","a":"1","b":"3"},{"abc":"vf","a":"2","b":"4"}]
I have written the JavaScript code for it:
Javascript:
var push_apply = Function.apply.bind([].push);
var slice_call = Function.call.bind([].slice);
Object.defineProperty(Array.prototype, "pushArrayMembers", {
value: function() {
for (var i = 0; i < arguments.length; i++) {
var to_add = arguments[i];
for (var n = 0; n < to_add.length; n+=300) {
push_apply(this, slice_call(to_add, n, n+300));
}
}
}
});
var globalResultArr = [];
var data = [{abc: "vf",def: [{ a:"1", b:"3"},{a:"2",b:"4"}]}];
function f(){
for(var i=0;i<data.length;i++){ //Iterate Data Array
var obj = data[i];
var resultArray = [{}];
for (var key in obj) { // Iterate Object in Data Array
if (obj.hasOwnProperty(key)) {
if(Object.prototype.toString.call(obj[key]) === "[object Array]"){
var tempRes = $.extend(true, [], resultArray);
resultArray = [];
for(var k=0;k<tempRes.length;k++){
var tempResObj = tempRes[k];
for(var j=0;j<obj[key].length;j++){ // If it has array, then iterate it as well
var innerObj = obj[key][j]; //Iterate Object in inner array
for(var innerkey in innerObj){
if (innerObj.hasOwnProperty(innerkey)) {
tempResObj[innerkey] = innerObj[innerkey];
}
}
resultArray.push(tempResObj);
}
}
}else{
for(var i=0;i<resultArray.length;i++){
resultArray[i][key] = obj[key];
}
}
}
}
globalResultArr.pushArrayMembers(resultArray);
}
document.getElementById("test").innerHTML = JSON.stringify(globalResultArr);
}
f();
HTML:
<div id="test"></div>
Issue is that browser crashes when item in data array > 10000. How can i make this work? Is there any simpler approach?
JSFiddle
Here's an attempt. It assumes there is only one property in each object that is an array. It them composes an object with the remaining properties and makes a new clone with each value from the array property.
* Works as required now *
var resultArray = [],
len = data.length,
tempObj,
newObj,
targetVal,
targetKey,
composeObj,
tempJson,
key,
i, k;
for(i = 0; i < len; i++) {
tempObj = data[i];
// obj to build up
composeObj = {};
// look at all key/vals in obj in data
for(key in tempObj) {
keyObj = tempObj[key];
// is it an array?
if(Array.isArray(keyObj)) {
// save key/val for array property
targetVal = keyObj;
targetKey = key;
}
else {
// copy non-array properties to empty object
composeObj[key] = keyObj;
}
}
// json-ify the object for cloning
tempJson = JSON.stringify(composeObj);
// compose new object for each val in array property
targetVal.map(function(val) {
// clone object
newObj = JSON.parse(tempJson);
// add array values as object properties
for(k in val) {
newObj[k] = val[k];
}
// add to results
resultArray.push(newObj);
});
}
I'm trying to use recursion in JavaScript to deeply go through an object and return its key and value.
An example of this would be:
var json2 = {
'key1': {
'key2Nested': {
'key3Nested': {
'key4Nested': 'SomeValue'
},
'key5Nested': 'unimportantValue',
'key6Nested': 'SimpleValue'
},
'key7Nested': '2SimpleValue',
'key8Nested': 'unimportantValue2'
}
};
The function will take the above input and return something like
['key1/key2Nested/key3Nested/key4Nested', 'SomeValue'],
['key1/key2Nested/key5Nested', 'unimportantValue'],
etc for all values.
The problem is I try to use a for loop on all the object's keys and I try to use recursion inside the loop. But the recursion value returns an array, which ends the for loop.
Here is the code that I have so far:
var makeArray = function(obj) {
var keysArray = Object.keys(obj);
var returnArray = [];
for (var i = 0; i < keysArray.length; i++) {
var key = keysArray[i];
var next_results;
var path, value;
if (typeof(value) != 'object' ) {
value = obj[key];
returnArray = orderedArray.concat([key, value]);
} else if (typeof(value) == "object") {
next_results = makeArray(obj[key]);
if (next_results) {
for (var j = 0; j < next_results.length; j++) {
next_results[j][1] = '/' + key + next_results[j][1];
returnArray = returnArray.concat(next_results[j]);
}
}
}
console.log(returnArray);
return returnArray;
}
}
The function needs to save the key returned from deeper recursion levels so that it can concatenate it to the path.
Perhaps my algorithm can be improved somehow or I'm thinking of it wrong. Can anyone give some advice? Thanks!
Just don't return returnArray inside the for loop body, but only after it.
Also, some other bugs:
The line
next_results[j][1] = '/' + key + next_results[j][1];
doesn't seem to be right. Your keys are in the first slot of each tuple, and you want the slash in between the keys not before them:
next_results[j][0] = key + '/' + next_results[j][0];
In
var path, value;
if (typeof(value) != 'object' ) {
value = obj[key];
you are testing the type of value before assigning it (so that you basically use the value from the previous iteration). Move the property access before the condition!
The method call
returnArray = returnArray.concat(…)
doesn't do what you think it does. You're passing in a tuple (array) that you want to get appended to the array, but the concat method merges the two arrays: [key1, value1].concat([key2, value]) == [key1, value1, key2, value2]. You want to use push instead to get an array of tuples.
In whole:
function makeArray(obj) {
var keys = Object.keys(obj);
var returnArray = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i],
value = obj[key];
if (typeof value != 'object' ) {
returnArray.push([key, value]);
} else {
var next_results = makeArray(value);
for (var j = 0; j < next_results.length; j++) {
next_results[j][0] = key + '/' + next_results[j][0];
returnArray.push(next_results[j]);
}
}
}
return returnArray;
}
I have an array (tlist) with keys linked with arrays:
tliste.push({"GROUP104":["321992","322052","321812","314022","0"]});
tliste.push({"GROUP108":["322011","322032","0"]});
tliste.push({"GROUP111":["322020","322021","322040","322041","313060","313072","0"]});
I now need to build a function to take the values of e.g. Group104 and Group111 and clone these into a new array:
newarrray = ["321992","322052","321812","314022","0","322020","322021","322040","322041","313060","313072","0"]
Preferably the new array should be ordered and the "0" should be removed - but that is of lower importance.
Let the groups to be extracted be grp[].
You can do something like this -
// Extract groups in grp[] from origArray[]
var extractGrps = function(grps, origArray) {
var result = [];
for(var i =0; i<grps.length; i+=1) {
var indxInOrigArray = indexOfObjectWithKey(origArray, grps[i]);
if(indxInOrigArray > 0) {
var arrLocal = origArray[indxInOrigArray].grps[i];
for(var j=0; j<arrLocal.length;j+=1)
result.push(arrLocal[j]);
}
}
return result;
}
//find Index of object in arr whose key matches the given input key
var indexOfObjectWithKey = function(arr, key) {
for(var i=0; i<arr.length; i+=1) {
if(arr[i].key) {
return i;
}
}
return -1;
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Array value count javascript
I have an array which contains several duplicates, what I'm trying to achieve is to count how many duplicates each unique string has in this one array.
The array looks something like this
array = ['aa','bb','cc','aa','ss','aa','bb'];
Thus I would like to do something like this
if (xWordOccurrences >= 5) {
// do something
}
But I'm not sure how I would code this.
I was thinking, create an object with each unique string, then loop through the original array, match each string with it's object and increment it's number by 1, then loop over the object to see which words had the most duplicates...
But this seems like an over complexe way to do it.
You can use an object which has keys of the Array's values and do something like this
// count everything
function getCounts(arr) {
var i = arr.length, // var to loop over
obj = {}; // obj to store results
while (i) obj[arr[--i]] = (obj[arr[i]] || 0) + 1; // count occurrences
return obj;
}
// get specific from everything
function getCount(word, arr) {
return getCounts(arr)[word] || 0;
}
getCount('aa', ['aa','bb','cc','aa','ss','aa','bb']);
// 3
If you only ever want to get one, then it'd be more a bit more efficient to use a modified version of getCounts which looks similar to getCount, I'll call it getCount2
function getCount2(word, arr) {
var i = arr.length, // var to loop over
j = 0; // number of hits
while (i) if (arr[--i] === word) ++j; // count occurance
return j;
}
getCount2('aa', ['aa','bb','cc','aa','ss','aa','bb']);
// 3
Try this function:
var countOccurrences = function(arr,value){
var len = arr.length;
var occur = 0;
for(var i=0;i<len;i++){
if(arr[i]===value){
occur++;
}
}
return occur;
}
var count = countOccurrences(['aaa','bbb','ccc','bbb','ddd'],'bbb'); //2
If you want, you can also add this function to the Array prototype:
Array.prototype.countOccurrences = function(value){
var len = this.length;
var occur = 0;
for(var i=0;i<len;i++){
if(this[i]===value){
occur++;
}
}
return occur;
}
How about you build an object with named property?
var array = ['aa','bb','cc','aa','ss','aa','bb'];
var summary = {};
var item = '';
for ( i in array){
item = array[i];
if(summary[item]){
summary[item] += 1;
}
else{
summary[item] = 1;
}
}
console.log( summary );
summary will contain like this
{aa: 3, bb: 2, cc: 1, ss: 1}
which you could then iterate on and then sort them later on if needed.
finally to get your count, you could use this summary['aa']
<script type="text/javascript">
var array = ['aa','bb','cc','aa','ss','aa','bb'];
var myMap = {};
for(i = 0; i < array.length; i++) {
var count = myMap[array[i]];
if(count != null) {
count++;
} else {
count = 1;
}
myMap[array[i]] = count;
}
// at this point in the script, the map now contains each unique array item and a count of its entries
</script>
Hope this solves your problem
var array = ['aa','bb','cc','aa','ss','aa','bb'];
var dups = {};
for (var i = 0, l = array.length; i < l; i++ ) {
dups[array[i]] = [];
}
for (str in dups) {
for (var i = 0, l = array.length; i < l; i++ ) {
if (str === array[i]) {
dups[str].push(str);
}
}
}
for (str in dups) {
console.log(str + ' has ' + (dups[str].length - 1) + ' duplicate(s)');
}
This function may do everything you need.
function countDupStr(arr, specifier) {
var count = {}, total = 0;
arr.forEach(function (v) {
count[v] = (count[v] || 0) + 1;
});
if(typeof specifier !== 'undefined') {
return count[specifier] - 1;
}
Object.keys(count).forEach(function (k) {
total += count[k] - 1;
});
return total;
}
Each value in the array is assigned and incremented to the count object. Whether or not a specifier was passed, the function will return duplicates of that specific string or the total number of duplicates. Note that this particular technique will only work on string-coercible values inside your arrays, as Javascript can only index objects by string.
What this means is that during object assignment, the keys will normalize down to strings and cannot be relied upon for uniqueness. That is to say, this function wouldn't be able to discern the difference between duplicates of 3 and '3'. To give an example, if I were to perform:
var o = {}, t = {};
o[t] = 1;
console.log(o);
The key used in place of t would eventually be t.toString(), thus resulting in the perhaps surprising object of {'[object Object]': 1}. Just something to keep in mind when working with Javascript properties.
I saw this post about it, perhaps it can help:
http://ryanbosinger.com/blog/2011/javascript-count-duplicates-in-an-array/