Array contains only 1 value, so push fails - javascript

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);
}
}

Related

Array in localStorage is not initialized at first run

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 ??= [];

Grouping by fields after reduce is not working in JavaScript

There is a complex object and based on an array which is given as an input I need to modify its properties. Illustration is shown below. If the "field" is same , add them to "or" array .If its different "field" add them to "and" array along with its "value". I am using Set to get keys from both source and input and using them to group based on its keys. Also whenever there are duplicates .ie., suppose the "filterObj" already has the same (field, value) pair. Be it in "and" or inside "or",Then don't add it in the final object
Sandbox: https://codesandbox.io/s/optimistic-mirzakhani-pogpw-so-dpvis
There is a TestCases file in the sandbox which its needs to pass
let filterObj = {
feature: "test",
filter: {
and: [{ field: "field2" }]
}
};
let obj = [{ field: "field2", value: "3" }];
let all_filters = [];
if (filterObj.filter.and && filterObj.filter.and.hasOwnProperty("or")) {
all_filters = [...filterObj.filter.and.or];
} else if (filterObj.filter.and) {
all_filters = [...filterObj.filter.and];
}
const all_objs = [...obj, ...all_filters];
const uniqKeys = all_objs.reduce(
(acc, curr) => [...new Set([...acc, curr.field])],
[]
);
const updateItems = uniqKeys.map(obj => {
const filter_items = all_objs.filter(item => item.field === obj);
let resultObj = {};
if (filter_items && filter_items.length > 1) {
resultObj.or = [...filter_items];
} else if (filter_items && filter_items.length === 1) {
resultObj = { ...filter_items[0] };
}
return resultObj;
});
var result = { ...filterObj, filter: { and: [...updateItems] } };
console.log(result);
Try it.
I redid the implementation, it happened more universally.
Parses any filters according to your algorithm that it finds.
All test cases are worked.
Sandbox link: https://codesandbox.io/s/optimistic-mirzakhani-pogpw-so-i1u6h
let filterObj = {
feature: "test",
filter: {
and: [
{
field: "field1",
value: "2"
}
]
}
};
let obj = [
{
field: "field1",
value: "2"
},
{
field: "field1",
value: "1"
}
];
var FilterController = function(filter) {
var self = this;
self.filter = filter;
// encapsulated map of objects by fields
var storeMap = {};
// counter of objects
var counter = 0;
var tryPutObjectToMap = function(object) {
if (typeof object === "object") {
// get type for grouping
var objectType = self.getObjectGroupType(object);
if (objectType !== null) {
// cheack have group
if (!storeMap.hasOwnProperty(objectType)) {
storeMap[objectType] = [];
}
var duplicate = storeMap[objectType].find(function(sObject) {
return self.getObjectValue(sObject) === self.getObjectValue(object);
});
// check duplicate
if (duplicate === undefined) {
counter++;
storeMap[objectType].push(object);
} else {
// TODO: Handle duplicates
}
} else {
// TODO: handle incorrect object
}
}
};
// get filter structure from map
var getFilterStructureFromMap = function() {
var result = {};
// check exists root filter and filed if have objects
if (counter > 0) {
result["and"] = [];
}
for (var key in storeMap) {
if (storeMap.hasOwnProperty(key)) {
var array = storeMap[key];
if (array.length > 1) {
result["and"].push({
// clone array
or: array.slice()
});
} else {
result["and"].push(array[0]);
}
}
}
return result;
};
// rewrite and get current filter
// if you need^ create new object for result
self.rewriteAndGetFilter = function() {
self.filter.filter = getFilterStructureFromMap();
return self.filter;
};
// not prototype function for have access to storeMap
self.putObjects = function(objects) {
if (Array.isArray(objects)) {
// recursive push array elements
objects.forEach(element => self.putObjects(element));
// handle array
} else if (typeof objects === "object") {
// handle object
if (objects.hasOwnProperty("and") || objects.hasOwnProperty("or")) {
for (var key in objects) {
//no matter `or` or `and` the same grouping by field
// inner object field
if (objects.hasOwnProperty(key)) {
self.putObjects(objects[key]);
}
}
} else {
// filters props not found, try push to store map
tryPutObjectToMap(objects);
}
} else {
// TODO: Handle errors
}
};
if (self.filter.hasOwnProperty("filter")) {
// put and parse current objects from filter
self.putObjects(self.filter.filter);
}
};
// function for grouping objects.
// for you get filed name from object.
// change if need other ways to compare objects.
FilterController.prototype.getObjectGroupType = function(obj) {
if (typeof obj === "object" && obj.hasOwnProperty("field")) {
return obj.field;
}
return null;
};
// get object value
FilterController.prototype.getObjectValue = function(obj) {
if (typeof obj === "object" && obj.hasOwnProperty("value")) {
return obj.value;
}
return null;
};
var ctrl = new FilterController(filterObj);
ctrl.putObjects(obj);
var totalFilter = ctrl.rewriteAndGetFilter();
console.log(totalFilter);
console.log(JSON.stringify(totalFilter));
EDIT 1
I did not change the logic; I made a function based on it.
let filterObj = {
feature: "test",
filter: {
and: [
{
field: "field1",
value: "2"
}
]
}
};
let obj = [
{
field: "field1",
value: 2
},
{
field: "field1",
value: "1"
}
];
function appendToFilter(filter, inputObjects) {
var storeMap = {};
var counter = 0;
var handlingQueue = [];
// if filter isset the appen to handling queue
if (filter.hasOwnProperty("filter")) {
handlingQueue.push(filter.filter);
}
// append other object to queue
handlingQueue.push(inputObjects);
// get first and remove from queue
var currentObject = handlingQueue.shift();
while (currentObject !== undefined) {
if (Array.isArray(currentObject)) {
currentObject.forEach(element => handlingQueue.push(element));
} else if (typeof currentObject === "object") {
if (currentObject.hasOwnProperty("and") || currentObject.hasOwnProperty("or")) {
for (var key in currentObject) {
if (currentObject.hasOwnProperty(key)) {
handlingQueue.push(currentObject[key]);
}
}
} else {
// TODO: append fild exists check
if (currentObject.field) {
if (!storeMap.hasOwnProperty(currentObject.field)) {
storeMap[currentObject.field] = [];
}
var localValue = currentObject.value;
// check duplicate
if (storeMap[currentObject.field].find(object => object.value === localValue) === undefined) {
counter++;
storeMap[currentObject.field].push(currentObject);
}
}
}
}
currentObject = handlingQueue.shift();
}
// create new filter settings
var newFilter = {};
// check exists root filter and filed if have objects
if (counter > 0) { newFilter["and"] = []; }
for (var storeKey in storeMap) {
if (storeMap.hasOwnProperty(storeKey)) {
var array = storeMap[storeKey];
if (array.length > 1) {
newFilter["and"].push({
// clone array
or: array.slice()
});
} else {
newFilter["and"].push(array[0]);
}
}
}
filter.filter = newFilter;
}
// update filterObj
appendToFilter(filterObj, obj);
console.log(filterObj);
EDIT 2,3 (UPDATED)
With others objects support.
export function appendToFilter(filter, inputObjects) {
var storeMap = {};
var others = [];
var counter = 0;
var handlingQueue = [];
// if filter isset the appen to handling queue
if (filter.hasOwnProperty("filter") && filter.filter.hasOwnProperty("and")) {
handlingQueue.push(filter.filter.and);
}
// append other object to queue
handlingQueue.push(inputObjects);
// get first and remove from queue
var currentObject = handlingQueue.shift();
while (currentObject !== undefined) {
if (Array.isArray(currentObject)) {
currentObject.forEach(element => handlingQueue.push(element));
} else if (typeof currentObject === "object") {
if (
currentObject.hasOwnProperty("and") ||
currentObject.hasOwnProperty("or")
) {
for (var key in currentObject) {
if (currentObject.hasOwnProperty(key)) {
handlingQueue.push(currentObject[key]);
}
}
} else {
// TODO: append fild exists check
if (currentObject.field) {
if (!storeMap.hasOwnProperty(currentObject.field)) {
storeMap[currentObject.field] = [];
}
var localValue = currentObject.value;
// check duplicate
if (
storeMap[currentObject.field].find(
object => object.value === localValue
) === undefined
) {
counter++;
storeMap[currentObject.field].push(currentObject);
}
} else {
// handle others objects^ without field "field"
counter++;
others.push(currentObject);
}
}
}
currentObject = handlingQueue.shift();
}
// create new filter settings
var newFilter = {};
// check exists root filter and filed if have objects
if (counter > 0) {
newFilter["and"] = [];
}
for (var storeKey in storeMap) {
if (storeMap.hasOwnProperty(storeKey)) {
var array = storeMap[storeKey];
if (array.length > 1) {
newFilter["and"].push({
// clone array
or: array.slice()
});
} else {
newFilter["and"].push(array[0]);
}
}
}
// Append others to result filter
others.forEach(other => newFilter["and"].push(other));
filter.filter = newFilter;
}

Extend Javascript Syntax to Add Typing

I'd like to extend javascript to add custom type checking.
e.g.
function test(welcome:string, num:integer:non-zero) {
console.log(welcome + num)
}
which would compile into:
function test(welcome, num) {
if(Object.prototype.toString.call(welcome) !== "[object String]") {
throw new Error('welcome must be a string')
}
if (!Number.isInteger(num)) {
throw new Error('num must be an integer')
}
console.log(welcome + num)
}
What's the most straightforward way of doing this?
So far i've looked at:
sweet.js (online documentation looks out of date as I think it's going through some sort of internal rewrite)
esprima and escodegen (not sure where to start)
manually parsing using regular expressons
After evaluating all the various options, using sweet.js appears to be the best solution. It's still fairly difficult to get working (and I am probably doing stuff the wrong way) but just in case someone want's to do something similar this here was my solution.
'use strict'
syntax function = function(ctx) {
let funcName = ctx.next().value;
let funcParams = ctx.next().value;
let funcBody = ctx.next().value;
//produce the normal params array
var normalParams = produceNormalParams(funcParams)
//produce the checks
var paramChecks = produceParamChecks(funcParams)
//produce the original funcBody code
//put them together as the final result
var params = ctx.contextify(funcParams)
var paramsArray = []
for (let stx of params) {
paramsArray.push(stx)
}
var inner = #``
var innerStuff = ctx.contextify(funcBody)
for (let item of innerStuff) {
inner = inner.concat(#`${item}`)
}
var result = #`function ${funcName} ${normalParams} {
${paramChecks}
${inner}
}`
return result
function extractParamsAndParamChecks(paramsToken) {
var paramsContext = ctx.contextify(paramsToken)
//extracts the actual parameters
var paramsArray = []
var i = 0;
var firstItembyComma = true
for (let paramItem of paramsContext) {
if (firstItembyComma) {
paramsArray.push({
param: paramItem,
checks: []
})
firstItembyComma = false
}
if (paramItem.value.token.value === ',') {
firstItembyComma = true
i++
} else {
paramsArray[i].checks.push(paramItem.value.token.value)
}
}
for (var i = 0; i < paramsArray.length; i++) {
var checks = paramsArray[i].checks.join('').split(':')
checks.splice(0, 1)
paramsArray[i].checks = checks
}
return paramsArray
}
function produceNormalParams(paramsToken) {
var paramsArray = extractParamsAndParamChecks(paramsToken)
//Produces the final params #string
var inner = #``
var first = true
for (let item of paramsArray) {
if (first === true) {
inner = inner.concat(#`${item.param}`)
} else {
inner = inner.concat(#`,${item.param}`)
}
}
return #`(${inner})`
}
function produceParamChecks(paramsToken) {
var paramsArray = extractParamsAndParamChecks(paramsToken)
var result = #``
for (let paramObject of paramsArray) {
var tests = produceChecks(paramObject)
result = result.concat(#`${tests}`)
}
return result
}
function produceChecks(paramObject) {
var paramToken = paramObject.param
var itemType = paramObject.checks[0]
var checks = paramObject.checks
if (itemType === undefined) return #``
if (itemType === 'array') {
return #`if (Object.prototype.toString.call(${paramToken}) !== "[object Array]") throw new Error('Must be array:' + ${paramToken})`
else {
throw new Error('item type not recognised: ' + itemType)
}
}
}

Restrict duplicate entries in json localstorage using Javascript

I went through few link, but that didnt help me. I have to restrict duplicate titles in json array. What is way to do here??
function submitForm(){
var titleInput=document.getElementById('titleName').value;
var messageInput=document.getElementById('titleDesc').value;
var oldItems = JSON.parse(localStorage.getItem('itemsArray')) || [];
var newItem = {
"title":titleInput ,
"desc": messageInput
};
if(!(titleInput=="" || messageInput=="")){
oldItems.push(newItem);
}
}
Try this:
if (!(titleInput == "" || messageInput == "")) {
var repeated = false;
for (var i = 0; i < oldItems.length; i++) {
if (oldItems[i].titleInput == titleInput) {
repeated = true;
break;
}
}
if (repeated == false) {
oldItems.push(newItem);
}
}
You could simply check wheter the item is there before adding it.
var alreadyExists = oldItems.some(function (item) { return item.title == titleInput; });
if(!(titleInput=="" || messageInput=="") && !alreadyExists) {
oldItems.push(newItem);
}
Then perhaps you should make the concept more explicit by encapsulating that logic within an ItemStore or something similar.
function ItemStore(items) {
this._items = [];
this._titleMap = {};
this.addAll(items || []);
}
ItemStore.prototype = {
constructor: ItemStore,
hasItemTitled: function (title) {
return !!this._titleMap[title];
},
add: function (item) {
var title = item.title;
if (this.hasItemTitled(title)) throw new Error("the store already contains an item titled '" + title + "'");
this._titleMap[title] = true;
this._items.push(item);
},
addAll: function (items) {
items.forEach(this.add.bind(this));
},
items: function () { return this._items.slice(); }
//other useful methods such as itemAt, remove...
};
Then your code becomes as simple as...
var titleInput=document.getElementById('titleName').value;
var messageInput=document.getElementById('titleDesc').value;
var oldItems = new ItemStore(JSON.parse(localStorage.getItem('itemsArray')) || []);
var newItem = {
"title":titleInput ,
"desc": messageInput
};
var shouldAddItem = titleInput != "" && messageInput !="" && !oldItems.hasItemTitled(newItem.title);
if (shouldAddItem) oldItems.add(newItem);
Now obviously, your function is still doing too much since it:
knows how to retrieve and create a new item from the user's input
knows how to rehydrate the item store
knows what to check to validate if an item is valid and should be added or not
You should be reading about the Single Responsability Principle, which isin't only applicable in OO.

how to get the querystring from a parent page?

i am using an iframe ipage in my parentpage. I would like to get the querystring in javascript of the parentpage?
I suggest to you to use my favourite function:
function getQueryString() {
var queryStringKeyValue = window.parent.location.search.replace('?', '').split('&');
var qsJsonObject = {};
if (queryStringKeyValue != '') {
for (i = 0; i < queryStringKeyValue.length; i++) {
qsJsonObject[queryStringKeyValue[i].split('=')[0]] = queryStringKeyValue[i].split('=')[1];
}
}
return qsJsonObject;
}
Just call it from the child window like this and act with the query string as an object.
For example if you have the query string ?name=stack and you want to get it, try:
getQueryString().name
This will return stack.
nice answer from #Marawan. - if it helps anyone...
I extended this to choose the target as a parameter (self / parent)
function getQueryString(target) {
if ( target == 'parent' ) {
var queryStringKeyValue = window.parent.location.search.replace('?', '').split('&');
}
else {
var queryStringKeyValue = window.location.search.replace('?', '').split('&');
}
var qsJsonObject = {};
if (queryStringKeyValue != '') {
for (i = 0; i < queryStringKeyValue.length; i++) {
qsJsonObject[queryStringKeyValue[i].split('=')[0]] = queryStringKeyValue[i].split('=')[1];
}
}
return qsJsonObject;
}
eg.
getQueryString('parent').id; // get iframe parent url ?id=foo
getQueryString().id; // get this url ?id=foo
ES6 implementation:
export const getQueryParameters = () => {
const queryStringKeyValue = window.parent.location.search.replace('?', '').split('&');
return queryStringKeyValue.reduce((acc, curr) => {
const [key,value] = curr.split('=')
return {
...acc,
[key]: value
}
}, {})
}
Usage:
getQueryParameters().name

Categories