I am trying to set an array as a key value in a JSON object using javascript.
When I set the array value,
console.log(obj["likes"]) displays an array of size 1.
but on the next line
console.log(obj) shows that the likes key is an array of size 0.
I have a JSON object that has information about posts.
If no likes exist on a post then that field does not exist in that post's objects.
I am trying to implement a like-dislike update function, where I check if a user has liked a post.
If he hasn't then I append his username to the array of likes else I remove his username.
userID is a global variable that I define at the start of the script tag.
It works if instead of userID, I set a new string like:
obj["likes"] = ["XX"]
This works too (i get an extra space but it atleast logs correctly):
obj["likes"] = [userId+" "]
console.log(obj["likes"])
console.log("Obj:",obj)
But then doing this again does not work!!!!
let arr = [" "+userId]
console.log(arr)
arr[0] = arr[0].trim()
console.log(arr)
obj["likes"] = arr
console.log("Obj:",obj)
function saveLikeDislike(url, action) {
for (i = 0; i < crawledUrlsData.length; i += 1) {
if (typeof crawledUrlsData[i] === "object") {
var obj = crawledUrlsData[i]
if (url === obj["url"]) {
if (action === "like") {
if ("likes" in obj) {
likes = obj["likes"]
if (likes.includes(userId)) {
likes = likes.filter(id => id != userId)
} else {
likes.push(userId)
}
obj["likes"] = likes
} else {
var id = window.userId
console.log(userId)
obj["likes"] = [id]
console.log(obj["likes"])
console.log("Obj:",obj)
}
if ("dislikes" in obj) {
var dislikes = obj["dislikes"]
if (dislikes.includes(userId)) {
dislikes = dislikes.filter(id => id != userId)
obj["dislikes"] = dislikes
}
}
} else {
if ("dislikes" in obj) {
dislikes = obj["dislikes"]
if (dislikes.includes(userId)) {
dislikes = dislikes.filter(id => id != userId)
} else {
dislikes.push(userId)
}
obj["dislikes"] = dislikes
} else
obj["dislikes"] = [dislikes]
}
if ("likes" in obj) {
var likes = obj["likes"]
if (likes.includes(userId)) {
likes = likes.filter(id => id != userId)
obj["likes"] = likes
}
}
}
crawledUrlsData[i] = obj
console.log(obj["likes"])
renderData()
return
}
}
}
Two problems.
1. That usrId - userId typo randomSoul has mentioned.
2. This line:
likes = likes.push(userId)
The output of likes.push(something) is a number, the length of the array after push. This line will amount to likes = 1. Do instead:
likes.push(userId)
push returns the new length of the array - so this line:
likes = likes.push(userId);
Will be a number, not an array - remove the assignment:
likes.push(userId);
Turnes out I had missed a parenthesis.
But this still does not explain the odd behavior where on one line key value was set and the very next line the output from console.log was different when accessing userId but proper if userId was modified in some way.
Anyway, here's the fixed function:
function saveLikeDislike(url, action) {
for (i = 0; i < crawledUrlsData.length; i += 1) {
if (typeof crawledUrlsData[i] === "object" && crawledUrlsData[i]["url"] == url) {
var obj = crawledUrlsData[i]
if (url === obj["url"]) {
if (action === "like") {
if ("likes" in obj) {
console.log("likes in obj")
likes = obj["likes"]
if (likes.includes(userId)) {
likes = likes.filter(id => id != userId)
} else {
likes.push(userId)
}
obj["likes"] = likes
} else {
obj.likes = [userId]
console.log("Obj:",obj)
}
if ("dislikes" in obj) {
var dislikes = obj["dislikes"]
console.log("Dislikes: ",dislikes)
if (dislikes.includes(userId)) {
dislikes = dislikes.filter(id => id != userId)
obj["dislikes"] = dislikes
}
}
} else if (action === "dislike"){
if ("dislikes" in obj) {
dislikes = obj["dislikes"]
if (dislikes.includes(userId)) {
dislikes = dislikes.filter(id => id != userId)
} else {
dislikes.push(userId)
}
obj["dislikes"] = dislikes
} else {
obj["dislikes"] = [userId]
}
if ("likes" in obj) {
var likes = obj["likes"]
console.log("ID: ",userId)
if (likes.includes(userId)) {
likes = likes.filter(id => id != userId)
obj["likes"] = likes
}
}
}
}
crawledUrlsData[i] = obj
linkTreeRef.set(crawledUrlsData)
}
}
}
Related
window.reload = () => {
var userArray = JSON.parse(localStorage.getItem("key"));
}
let feedback = document.getElementById("feedback");
function checkemail(userArray, email) {
var i;
if (userArray == null | undefined) {
userArray = JSON.parse(localStorage.getItem("key"));
}
var person = {
name: document.getElementById("nameinput").value,
email: document.getElementById("emailinput").value,
passowrd: document.getElementById("passwordinput").value
};
let isFound = false;
for (i = 0; i < userArray.length; i++) { //here is the error it still happen even after I added the if null part
if (userArray != undefined)
var oldemail = userArray[i].email;
let newemail = document.getElementById("emailinput").value;
if (newemail === oldemail) {
isFound = true;
i = userArray.length;
return feedback.innerHTML = "email exist please log in or register with different email";
}
}
if (!isFound) {
return storeName(person, userArray);
}
}
function storeName(person, userArray) {
if (userArray != undefined)
var person = {
name: document.getElementById("nameinput").value,
email: document.getElementById("emailinput").value,
passowrd: document.getElementById("passwordinput").value
};
userArray = JSON.parse(localStorage.getItem("key"));
userArray.push(person);
userArray = JSON.stringify(userArray);
localStorage.setItem("key", userArray);
console.log(userArray);
}
I want to store an array in local storage, the first time when I run the code, of course, the array is empty and I can not use a loop for example because I can't call (array.length).
so can I tell the compiler for example if the array is null or undefined just put length is zero or assign the value of the array to an empty array?
can I do something like this?
if( userArray == null | undefined) { userArray = JSON.parse(localStorage.getItem("key")); }
function checkemail(userArray, email) {
if (userArray == null || typeof(userArray) == 'undefined') {
userArray = [];
}
// rest of the code
}
This might be working too:
userArray ??= [];
I have an object productCounts
[{provisioned=2.0, product=str1, totalID=1.0},
{product=str2, provisioned=4.0, totalID=3.0},
{provisioned=6.0, product=str3, totalID=5.0}]
I have an array uniqueProduct
[str1, str2, str3, str4]
I am then looping a dataset to get the totalID count, add it to the product's totalID but if it doesn't exist, push it to the object.
var countID = 0;
uniqueProduct.forEach(
currentproduct => {
countID = 0;
for (var i = 0; i < shtRng.length; ++i) {
if (shtRng[i][ProductCol].toString() == currentproduct) { // && shtRng[i][IDcol].toString().length>4){
countID++;
}
}
if (countID == 0) {
return;
}
console.log(currentproduct + ": " + countID);
}
)
This works perfectly to return the countID per product in uniqueProduct
Rather than logging the result, I would like to add it to the object like this... If the current unique product is not in the productCounts object, add it.
let obj = productCounts.find((o, i) => {
if (o.product == currentproduct) {
productCounts[i] = { product: currentproduct, totalID: productCounts[i].totalID+countID, provisioned: productCounts[i].provisioned };
return true;
} else {
productCounts.push({ product: currentproduct, totalID: countID, provisioned: 0 });
return true;
}
});
In my head, this should work but it appears to skip some records or add the product multiple times. How do I add to the object correctly?
Expected output is the object to be something similar to:
[{provisioned=2.0, product=str1, totalID=35.0},
{product=str2, provisioned=4.0, totalID=8.0},
{provisioned=6.0, product=str3, totalID=51.0},
{provisioned=6.0, product=str4, totalID=14.0}]
The argument to find() is a function that returns a boolean when the element matches the criteria. The if statement should use the result of this, it shouldn't be in the condition function.
let obj = productCounts.find(o => o.product == currentProduct);
if (obj) {
obj.totalId += countID;
} else {
productCounts.push(productCounts.push({ product: currentproduct, totalID: countID, provisioned: 0 });
}
BTW, your life would be easier if you used an object whose keys are the product names, rather than an array of objects. You can easily turn the array of objects into such an object:
let productCountsObj = Object.fromEntries(productCounts.map(o => [o.product, o]));
if (currentProduct in productCountsObj) {
productCountsObj[currentProduct].totalID += countID;
} else {
productCountsObj[currentProduct] = { product: currentproduct, totalID: countID, provisioned: 0 };
}
Hi I need to convert the the numeric values of my object to string. But different properties has different transformation rules.
My sample object:
{
name: "Name"
sRatio: 1.45040404
otherMetric: 0.009993
}
I use JSON.stringify to convert my initial object.
let replacemet = {}
JSON.stringify(metrics[0], function (key, value) {
//Iterate over keys
for (let k in value) {
if ((k !== "sRatio") || (k !== "name")) {
replacemet[k] = (100*value[k]).toFixed(2) + "%"
} else {
if( k === "name") {
replacemet[k] = "yo!"+value[k]
} else{
replacemet[k] = value[k].toFixed(2)
}
}
}
})
But my conditions are not triggered and all properties are converting on the same manner.
The job of the replacer callback is not to fill in some global replacemet object but rather to return a new value.
I think you are looking for something along the lines of
JSON.stringify(sample, function (key, value) {
if (key == "sRatio") {
return value.toFixed(2);
} else if (key == "name") {
return "yo!"+value;
} else if (typeof value == "number") {
return (100*value).toFixed(2) + "%"
} else {
return value;
}
})
Try using switch block that will be really good for this. Detailed description on switch.
let replacemet = {}
JSON.stringify(metrics[0], function (key, value) {
//Iterate over keys
for (let k in value) {
switch(k) {
case "name":
replacemet[k] = "yo!"+value[k];
break;
case "sRatio":
replacemet[k] = value[k].toFixed(2);
break;
default:
replacemet[k] = value[k].toFixed(2);
}
}
})
Hope to help you . I add when dynamic property
metrics =
[
{
name: "Name",
sRatio: 1.45040404,
otherMetric:0.009993
},
{
name: "Name1",
sRatio: 2.45040404,
otherMetric: 1.009993
}
]
;
let source = JSON.stringify(metrics);
let arrJson = new Array();
//arrJson = {};
metrics.forEach(function(value){
let replacemet = {};
for(var k in value) {
if( k.toString().trim() == "name") {
replacemet[k] = "yo!"+value[k] ;
}
else
if ( ( k.toString().trim() !== "sRatio") && ( k.toString().trim() !== "name")) {
replacemet[k] = (100* value[k] ).toFixed(2).toString() + "%" ;
} else {
replacemet[k] = value[k].toFixed(2) ;
}
}
arrJson.push(JSON.stringify(replacemet)) ;
});
console.log(arrJson);
var user = {};
var usernameList = document.querySelectorAll('.msg.g_bot.bot.private.i ~ .msg .usr');
for (i of usernameList) {
if (i.childNodes[0].nodeName === 'SPAN') {
var theUser = (user[i.childNodes[0].innerHTML] !== undefined) ? user[i.childNodes[0].innerHTML] : user[i.childNodes[0].innerHTML] = {};
var msg = theUser.msg = [];
msg.push(i.nextElementSibling.nextElementSibling.innerHTML);
}
}
The object user.whatever.msg is an array but contains only 1 value. So it's always the last one. In this case push doesn't work, so I can't put all values into that array.
What's wrong with my code?
theUser.msg = []; does create a new array on every iteration. Just like you create a new theUser object only when it doesn't exist already, you should only create the msg array only once.
var users = {};
var usernameList = document.querySelectorAll('.msg.g_bot.bot.private.i ~ .msg .usr');
for (var i of usernameList) {
if (i.firstChild.nodeName === 'SPAN') {
var name = i.firstChild.innerHTML; // should be .textContent probably
var theUser = name in user
? user[name]
: user[name] = { msg: [] };
// ^^^^^^^^^
theUser.msg.push(i.nextElementSibling.nextElementSibling.innerHTML);
}
}
A clearer way of doing it would be to use .reduce:
const user = [...document.querySelectorAll('.msg.g_bot.bot.private.i ~ .msg .usr')]
.reduce((userObj, i) => {
if (i.childNodes[0].nodeName !== 'SPAN') return userObj;
const childHtml = i.childNodes[0].innerHTML;
const theUser = userObj[childHtml] || { msg: [] };
theUser.msg.push(i.nextElementSibling.nextElementSibling.innerHTML)
return userObj;
}, {});
You could modify your code to verify if the array was already created:
var user = {};
var usernameList = document.querySelectorAll('.msg.g_bot.bot.private.i ~ .msg .usr');
for (i of usernameList) {
if (i.childNodes[0].nodeName === 'SPAN') {
var theUser = (user[i.childNodes[0].innerHTML] !== undefined) ? user[i.childNodes[0].innerHTML] : user[i.childNodes[0].innerHTML] = {};
if(theUser.hasOwnProperty(‘msg’) === false) {
theUser.msg = [];
}
theUser.msg.push(i.nextElementSibling.nextElementSibling.innerHTML);
}
}
I have been trying to translate my code from es6 to es5 because of some framework restrictions at my work... Although I have been quite struggling to locate what the problem is. For some reason the code does not work quite the same, and there is no errors either ...
Can someone tell me If I have translated properly ?
This is the ES6 code :
function filterFunction(items, filters, stringFields = ['Title', 'Description'], angular = false) {
// Filter by the keys of the filters parameter
const filterKeys = Object.keys(filters);
// Set up a mutable filtered object with items
let filtered;
// Angular doesn't like deep clones... *sigh*
if (angular) {
filtered = items;
} else {
filtered = _.cloneDeep(items);
}
// For each key in the supplied filters
for (let key of filterKeys) {
if (key !== 'TextInput') {
filtered = filtered.filter(item => {
// Make sure we have something to filter by...
if (filters[key].length !== 0) {
return _.intersection(filters[key], item[key]).length >= 1;
}
return true;
});
}
// If we're at TextInput, handle things differently
else if (key === 'TextInput') {
filtered = filtered.filter(item => {
let searchString = "";
// For each field specified in the strings array, build a string to search through
for (let field of stringFields) {
// Handle arrays differently
if (!Array.isArray(item[field])) {
searchString += `${item[field]} `.toLowerCase();
} else {
searchString += item[field].join(' ').toLowerCase();
}
}
// Return the item if the string matches our input
return searchString.indexOf(filters[key].toLowerCase()) !== -1;
});
}
}
return filtered;
}
And this is the code I translated that partially 99% work ..
function filterFunction(items, filters, stringFields, angular) {
// Filter by the keys of the filters parameter
var filterKeys = Object.keys(filters);
// Set up a mutable filtered object with items
var filtered;
// Angular doesn't like deep clones... *sigh*
if (angular) {
filtered = items;
} else {
filtered = _.cloneDeep(items);
}
// For each key in the supplied filters
for (var key = 0 ; key < filterKeys.length ; key ++) {
if (filterKeys[key] !== 'TextInput') {
filtered = filtered.filter( function(item) {
// Make sure we have something to filter by...
if (filters[filterKeys[key]].length !== 0) {
return _.intersection(filters[filterKeys[key]], item[filterKeys[key]]).length >= 1;
}
return true;
});
}
// If we're at TextInput, handle things differently
else if (filterKeys[key] === 'TextInput') {
filtered = filtered.filter(function(item) {
var searchString = "";
// For each field specified in the strings array, build a string to search through
for (var field = 0; field < stringFields.length; field ++) {
// Handle arrays differently
console.log(field);
if (!Array.isArray(item[stringFields[field]])) {
searchString += item[stringFields[field]] + ' '.toLowerCase();
} else {
searchString += item[stringFields[field]].join(' ').toLowerCase();
}
}
// Return the item if the string matches our input
return searchString.indexOf(filters[filterKeys[key]].toLowerCase()) !== -1;
});
}
}
return filtered;
}
These two lines
searchString += `${item[field]} `.toLowerCase();
searchString += item[stringFields[field]] + ' '.toLowerCase();
are not equivalent indeed. To apply the toLowerCase method on all parts of the string, you'll need to wrap the ES5 concatenation in parenthesis:
searchString += (item[stringFields[field]] + ' ').toLowerCase();
or, as blanks cannot be lowercased anyway, just use
searchString += item[stringFields[field]].toLowerCase() + ' ';
Here is a translated code from babeljs itself, as commented above.
'use strict';
function filterFunction(items, filters) {
var stringFields = arguments.length <= 2 || arguments[2] === undefined ? ['Title', 'Description'] : arguments[2];
var angular = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3];
// Filter by the keys of the filters parameter
var filterKeys = Object.keys(filters);
// Set up a mutable filtered object with items
var filtered = void 0;
// Angular doesn't like deep clones... *sigh*
if (angular) {
filtered = items;
} else {
filtered = _.cloneDeep(items);
}
// For each key in the supplied filters
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
var _loop = function _loop() {
var key = _step.value;
if (key !== 'TextInput') {
filtered = filtered.filter(function (item) {
// Make sure we have something to filter by...
if (filters[key].length !== 0) {
return _.intersection(filters[key], item[key]).length >= 1;
}
return true;
});
}
// If we're at TextInput, handle things differently
else if (key === 'TextInput') {
filtered = filtered.filter(function (item) {
var searchString = "";
// For each field specified in the strings array, build a string to search through
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = stringFields[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var field = _step2.value;
// Handle arrays differently
if (!Array.isArray(item[field])) {
searchString += (item[field] + ' ').toLowerCase();
} else {
searchString += item[field].join(' ').toLowerCase();
}
}
// Return the item if the string matches our input
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
return searchString.indexOf(filters[key].toLowerCase()) !== -1;
});
}
};
for (var _iterator = filterKeys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
_loop();
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return filtered;
}
p.s. Or there is a better way to use babeljs directly without manually converting it.