jQuery how to $.map over a dynamic length of an array - javascript

I have the following:
transformResult: function(response) {
if (response && response.buckets && response.buckets[0] && response.buckets[0].documents) {
return {
suggestions: $.map(response.buckets[0].documents, function(dataItem) {
return { value: dataItem._id, data: {
key: response.buckets[0].key,
url: dataItem.url
}
};
})
};
}
I'm using response.buckets[0] to ensure at least one bucket exists in the array. There can be 0 or 1+ buckets. The problem is, now the suggestions are just returning for the first bucket w [0] in response.buckets[0].documents
How can I get the suggestions to return for 0 or more $.map(response.buckets[0].documents?
Update
transformResult: function(response) {
var suggestions = {
suggestions: {}
};
if(!response || !response.buckets) {
return suggestions;
}
for(var i=0;i<response.buckets.length;i++) {
var bucket = response.buckets[i];
if(!!bucket.documents) {
suggestions.concat($.map(bucket.documents, function(item) {
return {
value: item._id,
data: {
key: bucket.key,
url: item.url
}
}
}));
};
}
return suggestions;
},
This is now erroring with: Uncaught TypeError: suggestions.concat is not a function

If you run a for loop on the buckets-array and inside the for run the map function on each element you should achieve what you are after.
var suggestions = [];
if(!response || !response.buckets) {
return { suggestions: suggestions };
}
for(var i=0;i<response.buckets.length;i++) {
var bucket = response.buckets[i];
if(!!bucket.documents) {
suggestions.concat($.map(bucket.documents, function(item) {
return {
value: item._id,
data: {
key: bucket.key,
url: item.url
}
};
}));
}
}
return { suggestions: suggestions };
If there are 0 buckets, the for-loop will not loop at all and the suggestions array will be of 0 length.

I'm not entirely sure wether I got your intention and data-structure right, but I think you're looking for this:
transformResult: function(response) {
//seems that $.map() doesn't handle null-values :(
//so I have to take care of this
var emptyArray = [];
return {
suggestions: $.map(response && response.buckets || emptyArray, function(bucket){
//jQuerys map-implementation is actually more like a fmap.
//so this doesn't return an Array of Arrays, but one flat Array instead
return $.map(bucket.documents || emptyArray, function(document){
return {
value: document._id,
data: {
key: bucket.key,
url: document.url
}
}
});
})
}
}

Related

Getting last appearance of a deeply nested value by specified property

I've got this object as example:
const obj = {
group: {
data: {
data: [
{
id: null,
value: 'someValue',
data: 'someData'
}
]
}
}
};
My goal is to get a value by propery name.
In this case I would like to get
the value of the data propery, but the last appearance of it.
Meaning the expected output is:
someData
However, I'm using this recursive function to retreive it:
const findVal = (obj, propertyToExtract) => {
if (obj && obj[propertyToExtract]) return obj[propertyToExtract];
for (let key in obj) {
if (typeof obj[key] === 'object') {
const value = findVal(obj[key], propertyToExtract);
if (value) return value;
}
}
return false;
};
Which gives me the first appearance of data, meaning:
data: [
{
value: 'someValue',
data: 'someData'
}
]
How can I get the wanted result?
one way can be to recursively flat the object and just get the wanted index (here data)
const obj = {
group: {
data: {
data: [
{
id: null,
value: 'someValue',
data: 'someData'
}
]
}
}
};
function deepFlat(obj) {
let flatObj = {};
flat(obj,flatObj);
console.log(flatObj);
return flatObj;
}
function flat(toFlat, flatObj) {
Object.entries(toFlat).forEach(elem => {
if (elem[1] && typeof elem[1] === 'object') {
flat(elem[1], flatObj);
} else {
flatObj[elem[0]] = elem[1];
}
});
}
let result = deepFlat(obj);
console.log(result['data']);

nodejs filtering an array of objects where the filtering is partially done in an async function

I've read many similar questions and have tried a bunch of code. Unfortunately, I'm not getting my code to run :-(
So, the situation is as follows: In a route of a node.js server, I have to respond with a filtered array of Objects. Unfortunately, whatever I do, I always get an empty array [] back. The filter is a bit tricky in my opinion, as it consists of a string comparison AND an async call to a library function. With the console output, I can clearly see that the correct element is found, but at the same time I see that I've already received the object...
Here is some code that exemplifies my challenge:
let testArray = [
{
id: 'stringId1',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'noInterest'
}
}
},
{
id: 'stringId2',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
},
{
id: 'stringId3',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
}
]
// code from a library. Can't take an influence in it.
async function booleanWhenGood(id) {
if (id in some Object) {
return { myBoolean: true };
} else {
return { myBoolean: false };
}
}
// Should return only elements with type 'ofInterest' and that the function booleanWhenGood is true
router.get('/', function(res,req) {
tryOne(testArray).then(tryOneResult =>{
console.log('tryOneResult', tryOneResult);
});
tryTwo(testArray).then(tryTwoResult => {
console.log("tryTwoResult ", tryTwoResult);
});
result = [];
for (const [idx, item] of testArray.entries() ) {
console.log(idx);
if (item.data.someDoc.type === "ofInterest") {
smt.find(item.id).then(element => {
if(element.found) {
result.push(item.id);
console.log("ID is true: ", item.id);
}
});
}
if (idx === testArray.length-1) {
// Always returns []
console.log(result);
res.send(result);
}
}
})
// A helper function I wrote that I use in the things I've tried
async function myComputeBoolean(inputId, inputBoolean) {
let result = await booleanWhenGood(inputId)
if (result.myBoolean) {
console.log("ID is true: ", inputId);
}
return (result.myBoolean && inputBoolean);
}
// A few things I've tried so far:
async function tryOne(myArray) {
let myTmpArray = []
Promise.all(myArray.filter(item => {
console.log("item ", item.id);
myComputeBoolean(item.id, item.data.someDoc.type === "ofInterest")
.then(myBResult => {
console.log("boolean result", myBResult)
if (myBResult) {
tmpjsdlf.push(item.id);
return true;
}
})
})).then(returnOfPromise => {
// Always returns [];
console.log("returnOfPromise", myTmpArray);
});
// Always returns []
return(myTmpArray);
}
async function tryTwo(myArray) {
let myTmpArray = [];
myArray.forEach(item => {
console.log("item ", item.id);
myCompuBoolean(item.id, item.data.someDoc.type === "ofInterest")
.then(myBResult => {
console.log("boolean result", myBResult)
if (myBResult) {
myTmpArray.push(item.did);
}
})
});
Promise.all(myTmpArray).then(promiseResult => {
return myTmpArray;
});
}
Asynchronous programming is really tough for me in this situation... Can you help me get it running?
I didn't inspect your attempts that closely, but I believe you are experiencing some race conditions (you print return and print the array before the promises resolve).
However you can alwayd use a regular for loop to filter iterables. Like this:
let testArray = [
{
id: 'stringId1',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'noInterest'
}
}
},
{
id: 'stringId2',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
},
{
id: 'stringId3',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
}
]
async function booleanWhenGood(id) {
if (id in { 'stringId1': 1, 'stringId2': 1 }) { // mock object
return { myBoolean: true };
} else {
return { myBoolean: false };
}
}
async function main() {
let filtered = []
for (item of testArray)
if ((await booleanWhenGood(item.id)).myBoolean && item.data.someDoc.type === 'ofInterest')
filtered.push(item)
console.log('filtered :>> ', filtered);
}
main()

JS: Replace for loop with filter, map or foreach

I have such method:
function sort(arr) {
for (let i = 0; i < arr.length; i++) {
if (arr[i].title !== 'Test') {
arr[i] = {
...arr[i],
data: sortByType(arr[i].data),
full: sortByType(arr[i].full)
};
}
}
return arr;
}
Can it be simplified to use filter or foreach?
I have done it with map:
I havent tested it, but should work like you original code
function sort(arr) {
return arr.map(el => {
if(el.title === "Test") return el;
return { ...el, data: sortByType(el.data), full: sortByType(el.full) }
})
}
I think that your loop is the simplest right now.
Image you want to switch it to using a map, your code will become:
function sort(arr) {
return arr.map(elem => {
// you now need to handle this case too
if (elem.title === "Test")
return elem;
return {
...elem,
data: sortByType(elem.data),
full: sortByType(elem.full)
}
});
}
Which is essentially more code, and I don't think you gain anything from it imho.
A filter won't work in your case though.
this should help
function sort(arr) {
return arr.map((item) =>{
if(item.title != "Test") {
item = {
...item,
data: sortByType(item.data),
full: sortByType(item.full)
}
}
return item;
})
}

JavaScript/jQuery find JSON key/value with partial match of variable and return value

I have a list of models I want to search through and pull the url for the correct one. I won't always have the full key, and never the full value, but will always have at least a unique part of it.
Right now the code is just in test mode, with a set number that matches a key, print a success or failure.
The console keeps telling me that models[i].indexOf isn't a function. I know it's an object, but when I do a toString on it, I get "object Object". What am I not understanding?
I'm happy with a solution that is either vanilla JavaScript or uses jQuery.
The code:
if ($('.mobile_tutorial').length) {
var device = /*$device.model*/ "NTZEZ717VLU", model_code = device.substr(2).substr(0,device.length-3);
$.ajax({
url: "/scripts/phone_models.json",
type: 'get',
dataType: 'json',
success: function (data) {
var models = data.Manufacturer;
for (var i = models.length - 1; i >= 0; i--) {
if (models[i].indexOf(model_code) > -1) {
console.log(models[i])
} else {
console.log('no match')
}
}
}
});
}
The JSON (partial):
{
"Manufacturer": [{
"ZEZ955L": "http://x.com/mobile/home.seam?custId=ZEZ955L"
}, {
"ZEZ990G": "http://x.com/mobile/home.seam?custId=ZEZ990G"
}, {
"ZEZ828TL": "http://x.com/mobile/home.seam?custId=ZEZ828TL"
}, {
"ZEZ716BL": "http://x.com/mobile/home.seam?custId=ZEZ716BL"
}, {
"ZEZ717VL": "http://x.com/mobile/home.seam?custId=ZEZ717VL"
}, {
"ZEZ962BL": "http://x.com/mobile/home.seam?custId=ZEZ962BL"
}, {
"ZEZ963VL": "http://x.com/mobile/home.seam?custId=ZEZ963VL"
}]
}
models[i] is not a string so you are getting error. If you want to check key then use .each() function on models[i]. In that each loop compare the key using indexOf function.
if ($('.mobile_tutorial').length) {
var device = /*$device.model*/ "NTZEZ717VLU", model_code = device.substr(2).substr(0,device.length-3);
$.ajax({
url: "/scripts/phone_models.json",
type: 'get',
dataType: 'json',
success: function (data) {
var models = data.Manufacturer;
for (var i = models.length - 1; i >= 0; i--) {
$.each(models[i], function( key, value ) {
if (key.indexOf(model_code) > -1) {
console.log(models[i])
} else {
console.log('no match')
}
}
}});
});
}
You would need to grab the value of the key changing models[i].indexOf(model_code) to Object.keys(models[i])[0].indexOf(partial_model_code). Here's it in action:
var partial_model_code = '3VL'
function ajax(data) {
var models = data.Manufacturer;
for (var i = models.length - 1; i >= 0; i--) {
// grab the keys in the object
// since there will only be one object grab the first one
// check if the key partially matches
if (Object.keys(models[i])[0].indexOf(partial_model_code) > -1) {
console.log(models[i])
} else {
console.log('no match')
}
}
}
var data = JSON.parse(`{
"Manufacturer": [{
"ZEZ955L": "http://x.com/mobile/home.seam?custId=ZEZ955L"
}, {
"ZEZ990G": "http://x.com/mobile/home.seam?custId=ZEZ990G"
}, {
"ZEZ828TL": "http://x.com/mobile/home.seam?custId=ZEZ828TL"
}, {
"ZEZ716BL": "http://x.com/mobile/home.seam?custId=ZEZ716BL"
}, {
"ZEZ717VL": "http://x.com/mobile/home.seam?custId=ZEZ717VL"
}, {
"ZEZ962BL": "http://x.com/mobile/home.seam?custId=ZEZ962BL"
}, {
"ZEZ963VL": "http://x.com/mobile/home.seam?custId=ZEZ963VL"
}]
}`)
ajax(data)
I hope that helps!

Tree Recursion: How to get the parent root of the selected tree node

I have a tree-like structure of a json object
{
"saxena": {
"chewning": {
"betten": {},
"ching": {},
"kelley": {}
},
"kobrinsky": {
"karniely": {},
"naveh": {},
"rozenfeld": {},
"shalom": {}
},
"schriever": {
"brinker": {},
"mcleland": {},
"merrick": {}
},
"vacant": {
"akers": {},
"carlton": {
"marvin": {}
},
"fox": {
"glover": {
"clements": {},
"koya": {}
},
"holden": {}
}
}
},
"bill": {
"phil": {
"bob": {},
"smith": {},
"hello": {}
},
"bye": {
"ok": {},
"hmm": {},
"no": {},
"alright": {}
}
}
}
The root names are saxena and bill. I would like to create a function that can determine the root name of who the user searches for.
For the most simplest case, if they search for saxena, it returns saxena. If they return bill, it returns bill.
For a more complex case, saxena will be returned if the user searches for any of the names under her.
For example, if I search for betten, akers, glovers, or koya, saxena will be returned.
And if I search for bob, smith, or alright, bill will be returned.
This is my work so far. I tried using recursion, but for some reason when I find the selected name, I return an undefined.
var findRootName = function(data, ltmName) {
for (var key in data) {
if (key == ltmName) {
return key;
} else {
findNode(data[key], ltmName);
}
}
}
var findNode = function(data, ltmName) {
for (var key in data) {
if (key == ltmName) {
return key;
} else {
findNode(data[key], ltmName);
}
}
}
http://jsfiddle.net/gthnfta7/7/
Can somebody help me and figure out why my recursive function isn't working?
The problem is that you're not returning anything in the event that the node is found. You can simplify your function by writing it like this:
var findParent = function(data, childName) {
for (var key in data) {
if (key === childName || findParent(data[key], childName)) {
return key;
}
}
};
An alternative technique, if you need to make many calls over the same data, is something like the following:
function makeSearcher(data) {
var paths = (function makePaths(data, parentPath, store) {
var path = parentPath || [];
results = store || {};
Object.keys(data).forEach(function(key) {
var newPaths = path.concat(key);
results[key] = newPaths;
makePaths(data[key], newPaths, results);
});
return results;
})(data);
return function(key) {
var path = paths[key];
return path && path[0];
};
}
var search = makeSearcher(data);
search('clements'); //=> 'savena'
Note that the internal makePaths function is broader than the use here, as it could also be use to return a result like
[ "saxena", "vacant", "fox", "glover", "clements" ]

Categories