I have a json like this:
{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"}]}}
... and I dynamically fetch the values and create a variable this:
var existingParams = [
"name",
"updated"].filter(field => getBody.search[field]);
var sqlVal = existingParams.map(field => {
if (field === 'name') {
function getValues(item, index) {
var getVal = [item.tag];
return "%" + getVal + "%";
}
console.log(name.map(getValues));
return name.map(getValues);
} else {
return getBody.search[field];
}
})
For the above example I get for sqlVal:
console.log(sqlVal);
[ [ '%Peter%' ], '2018-11-07' ]
... which is fine.
BUT, if I have two values:
{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"},{"tag":"Jack"}]}}
... I'm getting this structure:
[ [ '%Peter%', '%Jack%' ], '2018-11-07' ]
... but what I need is sth like:
[ '%Peter%', '%Jack%', '2018-11-07' ]
... or:
[ ['%Peter%'], ['%Jack%'], '2018-11-07' ]
And in case of further e.g. 3 names:
{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"},{"tag":"Jack"},{"tag":"Maria"}]}}
... I need sth like:
[ '%Peter%', '%Jack%', '%Maria%', '2018-11-07' ]
... or:
[ ['%Peter%'], ['%Jack%'], ['%Maria%'], '2018-11-07' ]
... and so on
How do I need to adjust the above query to get this?
If I understand your question correctly, then this problem can be solved via the Array#reduce() method.
The general idea with this approach is to transform your input object to an array - the reduce operation can be used to do this, with the special-case rule of "flattening" the nested value on the name key into the final result:
var input = {"search":{"updated":"2018-11-07","name":[{"tag":"Peter"},{"tag":"Jack"}]}}
var result = Object
.entries(input.search)
.reduce(function(result, entry) {
const key = entry[0]
const value = entry[1]
if(key === 'name') {
// When the 'name' key is encountered, handle the value
// differently, by addting the items of this value array
// to the result
value.forEach(function(item) {
result.push('%' + item.tag + '%')
})
}
else {
// Append values for other keys directly to the result
result.push(value)
}
return result
}, [])
console.log(result )
You could simply use Object.values + reduce for something like this:
const json = { "search": { "updated": "2018-11-07", "name": [{ "tag": "Peter" }, { "tag": "Jack" }, { "tag": "Maria" }] } }
const result = Object.values(json.search).reduce((r,c) =>
(Array.isArray(c) ? r.push(...c.map(({tag}) => `%${tag}%`)) : r.push(c), r),[])
console.log(result)
If the order is important (names first then date) you could use reverse:
const json = { "search": { "updated": "2018-11-07", "name": [{ "tag": "Peter" }, { "tag": "Jack" }, { "tag": "Maria" }] } }
const result = Object.values(json.search).reverse().reduce((r,c) =>
(Array.isArray(c) ? r.push(...c.map(({tag}) => `%${tag}%`)) : r.push(c), r),[])
console.log(result)
First of all you did not provide a Minimal, Complete, and Verifiable example so it is quite hard for me to figure out where you are running into issues. For example, you are referencing existingParam but nowhere are they defined. This is key to understanding the problem because all of the code that you posted is heavily invested in the values and format of this value.
Second, how are you parsing the JSON? With the standard JSON#parse function you would get back an object with the same structure as your provided JSON. However, you are either not using this or you are mutating the object after it was parsed into a new format. Either way, the object that JSON#parse returns for the provided JSON is not an array and therefor you cannot use Array#map on it.
For the sake of being productive though I am going to try and explain how to do things.
JSON:
let data1 = '{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"}]}}',
data2 = '{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"},{"tag":"Jack"}]}} ',
data3 = '{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"},{"tag":"Jack"},{"tag":"Maria"}]}}';
Now that we have our JSON data we need to parse it and store it as a JSON object. To do so I am going to create a function; this way the data can be passed to the same function and handled the same way but the implementation will stay the same. Also, since we are only looking at the values in the search property we are going to go ahead and jump right into it.
Parse the JSON:
function parseResponse (response) {
let parsedResponse = JSON.parse(response);
parsedResponse = parsedResponse['search'];
}
Now that we have our function that takes our response and parses it we can then begin to sort through it to find and isolate the parts that we want. In this case we will add some code to loop through our properties and find the updated and name properties.
function parseResponse (response) {
let parsedResponse = JSON.parse(response);
parsedResponse = parsedResponse['search'];
for (let prop in parsedResponse) {
if (prop === 'updated') {
// do stuff with 'updated'
}
if (prop === 'name') {
// do stuff with 'name'
}
}
}
Because we want to return a result we are going to add a variable updated and names which will hold the values that we pull out of the string until we are ready to return them. Now that we have our loop and our temporary variables we can go ahead and pull the updated value out of our data and place it in the updated variable.
function parseResponse (response) {
let parsedResponse = JSON.parse(response),
updated = '',
names = [];
parsedResponse = parsedResponse['search'];
for (let prop in parsedResponse) {
if (prop === 'updated') {
updated = parsedResponse[prop];
}
if (prop === 'name') {
// do stuff with 'name'
}
}
}
With our updated value squared away we can jump into our names. Since you listed the format ['%name%', '%name%', '%name%'] first I am going to go ahead and show you how to do it this way. Here we are going to grab the property name, iterate through the names, grab the tag property, and then add the %s before pushing it to our names temporary variable.
function parseResponse (response) {
let parsedResponse = JSON.parse(response),
updated = '',
names = [];
parsedResponse = parsedResponse['search'];
for (let prop in parsedResponse) {
if (prop === 'updated') {
updated = parsedResponse[prop];
}
if (prop === 'name') {
for (let i = 0; i < parsedResponse[prop].length; i++) {
let name = parsedResponse[prop][i].tag;
name = '%' + name + '%';
names.push(name);
}
}
}
}
With everything in place all that is left is to assemble the result. To do so we are going to flatten the array of names, add them to the array, and then add the updated value to the end before returning it. To flatten the array we are going to use the spread operator.
function parseResponse (response) {
let parsedResponse = JSON.parse(response),
updated = '',
names = [];
parsedResponse = parsedResponse['search'];
for (let prop in parsedResponse) {
if (prop === 'updated') {
updated = parsedResponse[prop];
}
if (prop === 'name') {
for (let i = 0; i < parsedResponse[prop].length; i++) {
let name = parsedResponse[prop][i].tag;
name = '%' + name + '%';
names.push(name);
}
}
}
return [...names, updated];
}
With all of that set we can go ahead and call parseResponse() with data1, data2, or data3 and get back a response that looks like so:
let data1 = '{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"}]}}',
data2 = '{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"},{"tag":"Jack"}]}} ',
data3 = '{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"},{"tag":"Jack"},{"tag":"Maria"}]}}';
function parseResponse (response) {
let parsedResponse = JSON.parse(response),
updated = '',
names = [];
parsedResponse = parsedResponse['search'];
for (let prop in parsedResponse) {
if (prop === 'updated') {
updated = parsedResponse[prop];
}
if (prop === 'name') {
for (let i = 0; i < parsedResponse[prop].length; i++) {
let name = parsedResponse[prop][i].tag;
name = '%' + name + '%';
names.push(name);
}
}
}
return [...names, updated];
}
console.log(parseResponse(data1));
console.log(parseResponse(data2));
console.log(parseResponse(data3));
Spread operator can be used to flatten the result :
var obj = {"search":{"updated":"2018-11-07","name":[{"tag":"Peter"},{"tag":"Jack"},{"tag":"Maria"}]}}
var arr = [...obj.search.name.map(n => `%${n.tag}%`), obj.search.updated]
console.log( arr )
Another alternative could be to extract during parsing :
var arr = [], json = '{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"},{"tag":"Jack"},{"tag":"Maria"}]}}'
JSON.parse(json, (k, v) => v.trim && arr.push(k === 'tag' ? `%${v}%` : v))
console.log( arr )
I'm building a webshop where users are able to add products for one of more stores in their basket and checkout (like AliExpress).
On the cart overview page, the content of the basket is shown sorted by store. If the same product is added multiple times over different stores, the product is show by every store.
Now, I want to create an order for every store with the products ordered by that store. I'm using Angular to create the list with products ordered/filtered by store.
That data will be sent to my Node.JS server, to loop the contents and create some orders with items.
The problem, I think, is that the data is processed like a 'object' and not an 'array'. I have found a function which converts a object to an array, but the length is still '0'.
How can I process the data so I can loop through the different items?
AngularJS code to sort cart by store
$scope.filterProducts = function(groupName) {
$scope.productList = [];
$http({
method: 'GET',
xhrFields: {withCredentials: true},
url: '/loadCart'
}).then(function successCallback(response){
if (response.data) {
var mapByShop = function(arr, groupName) {
return arr.reduce(function(result, item) {
result[item[groupName]] = result[item[groupName]] || {};
result[item[groupName]][item['productId']] = item;
console.log('GROUPNAME en RESULT', groupName, result);
return result;
}, {});
};
if (response.data.length > 0) {
if (groupName == 'shopName') {
$scope.productList = mapByShop(response.data, groupName);
} else {
$scope.checkoutList = mapByShop(response.data, groupName);
}
}
}
}, function errorCallback(response){
console.log(response);
});
}
The $scope.productList is sent as 'data' in a $http POST function.
Node.JS code to convert an object to an array
function convertObjectToArray(object, cb){
var cartContent = [];
for (var i in object) {
cartContent[i] = object[i];
}
console.log("convertObjectToArray");
return cb(cartContent);
}
Code to process the data (where length is zero)
convertObjectToArray(req.body.cart, function(result){
console.log(isArray(result));
console.log('result', result);
console.log("lenght", result.length);
})
FYI: the isArray function
function isArray(myArray) {
return myArray.constructor.toString().indexOf("Array") > -1;
}
if array order is not important, you should use
cartContent.push(object[i]);
It will update the .length property automaticly
Your problem is that you are adding properties to the array object, and not using the Array API to insert at integer locations in the object. This means the array essentially remains "empty". If you key on an integer when inserting into the array then your code will work better.
The broken bit:
for (var i in object) {
cartContent[i] = object[i];
}
i is a string key here and will not increment the length of the Array unless the value coerces to an integer value (I think).
Something like this might work:
// Untested...
var keys = Object.keys(object);
for (var i = 0; i < keys.length; i++) {
cartContent[i] = object[keys[i]];
}
Or like the other answer suggested, use the push API.
Aside:
If you are in a modern JS engine you can:
Use Object.values, or
Write a couple of utility functions and convert an object to an array using the following
:
var o = iterable({foo:'fooValue', bar: 'barValue'});
console.log([...o]);
The utility functions:
function iterable(o) {
if(o[Symbol.iterator]) {
return o;
}
o[Symbol.iterator] = iter.bind(null, o);
return o;
}
function* iter(o) {
var keys = Object.keys(o);
for (var i=0; i<keys.length; i++) {
yield o[keys[i]];
}
}
How to iterate through json response to find value. I have this function getObjects to iterate through the response json obj and key which is the swaggerDefSplit (api/user).
getObjects(obj, key) {
var objects = [];
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
var val = obj[i];
objects.push(val);
}
}
return objects;
}
getSwagger() {
this._searchService.getSwagger()
.subscribe(data => {
// data = json response
console.log(data.paths);
let swaggerFullUrl = (<any>window).parent.swaggerURL;
let hashSplit = swaggerFullUrl.split('#');
let swaggerDefSplit = hashSplit[1].split('/');
// debugger;
var exist = this.getObjects(data, "/api/"+swaggerDefSplit[1]);
console.log(exist[4]);
},
err => {
console.log('err', err);
}
);
}
This console.log(exist[4]); gives me every api/paths but what I'm expecting is to give me the api/path relative to my swaggerSplit say (user) for example instead of giving me every paths.
json response I get from the console.log:
/api/User:{} // map this instead of everything
/api/Items:{}
/api/Stocks:{}
Hope I'm making sense at all.
I've got a remote JSON file that contains the list of the last 100 users who logged into a service. This JSON is updated constantly and lists the users from the most recently logged in to the "least recently" logged in.
If the user who appears as number X logs back in, they get removed from their position X and put back at the very top of the JSON at position [0].
I retrieve the JSON every 5 minutes. What I'd like to do is detect the differences between the old object oldUsers and the new newUsers and store them in another object that would only contain the users who are present in newUsers but not in oldUsers. I have no real idea as to how to achieve this.
Here's the JSON structure:
[{
"id":"foo09",
"name":"John",
"age":28
}, {
"id":"bar171",
"name":"Bryan",
"age":36
},
...
]
Is there a rather straightforward way to do it? Thanks!
You need to write your own diff algorithm. Here is one I whipped up in JSBin:
I will need a utility function to merge two arrays (Underscore would help here).
function mergeArrays(val1, val2) {
var results = val1.splice(0);
val2.forEach(function(val) {
if (val1.indexOf(val) < 0) {
results.push(val);
}
});
return results;
}
Diff algorithm
function diff(val1, val2) {
var results = [];
var origKeys = Object.keys(val1);
var newKeys = Object.keys(val2);
mergeArrays(origKeys, newKeys)
.forEach(function(key) {
if (val1[key] === val2[key]) { return; }
var result = {
key: key,
orig: val1[key],
'new': val2[key]
};
if (val1[key] == null) {
result.type = 'add';
} else if (val2[key] == null) {
result.type = 'delete';
} else {
result.type = 'change';
}
results.push(result);
});
return results;
}
When fetching an item from a DOJO datastore, DOJO adds a great deal of extra fields to it. It also changes the way the data is structure.
I know I could manually rebuild ever item to its initial form (this would require me to make updates to both JS code everytime i change my REST object), but there certainly has to be a better way.
Perhaps a store.detach( item ) or something of the sort?
The dojo.data API is being phased out, partly because of the extra fields. You could consider using the new dojo.store API. The store api does not add the extra fields.
I have written a function that does what you are looking to do. It follows. One thing to note, my function converts child objects to the { _reference: 'id' } notation. You may want different behavior.
Util._detachItem = function(item) {
var fnIncludeProperty = function(key) {
return key !== '_0'
&& key !== '_RI'
&& key !== '_RRM'
&& key !== '_S'
&& key !== '__type'
};
var store = item._S;
var fnCreateItemReference = function(itm) {
if (store.isItem(itm)) {
return { _reference: itm.id[0] };
}
return itm;
};
var fnProcessItem = function(itm) {
var newItm = {};
for(var k in itm) {
if(fnIncludeProperty(k)) {
if (dojo.isArray(itm[k])) {
// TODO this could be a problem with arrays with a single item
if (itm[k].length == 1) {
newItm[k] = fnCreateItemReference(itm[k][0]);
} else {
var valArr = [];
dojo.forEach(itm[k], function(arrItm) {
valArr.push(fnCreateItemReference(arrItm));
});
newItm[k] = valArr;
}
} else {
newItm[k] = fnCreateItemReference(itm[k]);
}
}
}
return newItm;
};
return fnProcessItem(item);
};
NOTE: this function is modified from what I originally wrote and I did not test the above code.