I have a problem searching for a nested array in my Angular app. I have to access the package.name. Please help thanksenfoiewfhfofhfowfoewhfowfoewhfwefwfhowefweofhew8ofwofhewofw8eofhwf
JSON
[{
"id": 1,
"name": "Yeah",
"package_id": 1,
"price": 100,
"package": {
"id": 1,
"name": "Yeah"
}
}]
TS
search(event) {
const val = event.target.value.toLowerCase();
if (!val) {
this.users = this.tempUsers;
}
const temp = this.tempUsers.filter((row) => {
return Object.keys(row).some((property) => {
return row[property] === null ? null : row[property].toString().toLowerCase().indexOf(val) !== -1;
});
});
this.users = temp;
}
TS
getAllUsers() {
this.usersService.getAll()
.subscribe(
(data: any) => {
console.log(data);
this.users = data.Users;
this.tempUsers= [...this.users];
},
error => {
console.log(error);
});
}
Does this work for you,
const temp = this.tempUsers.filter((row) => {
return Object.keys(row).some((property) => {
if (property === 'package') {
// check package.name value here
// like return row[property].name === 'something' ? true : false;
} else {
// the rest of the outer properties can be checked here
// not modifying your code here, but make sure it works for you
return row[property] === null ? null : row[property].toString().toLowerCase().indexOf(val) !== -1;
}
});
});
I have only answered your question on how to access the nested object inside the array method. Hope it helps or gives you an idea to fine tune your solution.
According to your data, You can use the following filter;
const filteredData = data.filter((item) => item.price.toString() === val || item.package_id.toString() === val || item.package.name.toString().toLowerCase().indexOf(val) !== -1 );
// UPDATED WITH CODE
search(event) {
const val = event.target.value.toLowerCase();
const filteredData = this.users.filter((item) => item.price.toString() === val ||
item.package_id.toString() === val ||
item.package.name.toString().toLowerCase().indexOf(val) !== -1 );
this.users = filteredData ;
}
Related
i want to know how i can get a value/numbers through key/username ?
look like that console.log(acc.${username})
my js
...
app.get("/account", (req, res) => {
loggedIn = req.cookies.loggedIn;
username = req.cookies.username;
if (loggedIn == "true") {
db.list().then(keys => {
if (keys.includes(username)) {
res.render("settings.html", { username: username })
var data = fs.readFileSync('2FA.json');
var acc = JSON.parse(data)
//////////////////////////////////////////////
console.log(acc.${username}) // i want to see a numbers in user name look like in here
/////////////////////////////////////////////
} else {
res.redirect("/logout");
}
});
} else {
res.render("notloggedin.html");
}
});
my json
[
{
"youssefnageeb": 927342
},
{
"youssefnageeb1":310686
},
{
"youssefnageeb2": 105380
},
{
"youssefnageeb3": 431816
},
{
"youssefnageeb4": 484728
}
]
I am guessing "data" is in the format mentioned above.
So, you can do
const index = data.findIndex(function isAccountRow(row) { return typeof row[username] === 'number'; })
if(index === -1) {
return res.redirect("/logout");
}
const row = data[index];
console.log(row[username]); // will be the number
In the following code I am trying to filter the data. However, I see in the algorithm that it loops through the first item in the userPassedFilter array and returns the filtered data but doesn't loop again to check the second item in the userPassedFilter array. I know this is happening because I call return true before it iterates through the entire userPassedFilter array. How can I assure that in the algorythm that it loops through all the items in the userPassedFilter before returning the final result?
**User Selected Filter**
let userPassedFilter = [{"phase":1},{"phase":2}]
**Data**
let dataList = [
{"voltage":35, "phase":2},
{"voltage":12, "phase":2},
{"voltage":12, "phase":3},
{"voltage":35, "phase":1}
]
**Filter Data Algorithm**
module.exports = {
filterData: (dataList, userPassedFilter)=>{
let filteredData = [];
filteredData = dataList.filter((data) =>{
for(let item of userPassedFilter){
for(let key in item){
if(data[key] === undefined || data[key] !== item[key])
return false
}
}
return true
})
return filteredData
}
}
filter over the dataList and return every object where some of its object properties are included in the userFilter.
const userPassedFilter = [{voltage:122},{phase:1},{phase:2},{voltage:44}];
const dataList = [
{voltage:44, phase:12},
{voltage:35, phase:2},
{voltage:12, phase:2},
{voltage:12, phase:3},
{voltage:35, phase:1},
{voltage:122, phase:156}
];
function filterData(dataList, userPassedFilter) {
return dataList.filter(obj => {
return userPassedFilter.some(inner => {
const [[ key, value ]] = Object.entries(inner);
return obj[key] === value;
});
});
}
console.log(filterData(dataList, userPassedFilter));
Introduce variable for each loop, so the result will match any of the elements in the userPassedFilter by matching all properties for the element.
let userPassedFilter = [{"phase":1},{"phase":2}];
let dataList = [{"voltage":35, "phase":2},{"voltage":12, "phase":2},{"voltage":12, "phase":3},{"voltage":35, "phase":1}];
const f = (dataList, userPassedFilter) => {
let filteredData = dataList.filter((data) => {
let filtered = true;
for (let item of userPassedFilter) {
let matched = true;
for (let key in item) {
if (data[key] === undefined || data[key] !== item[key]) {
matched = false;
break;
}
}
if (matched) {
filtered = false;
break;
}
}
return !filtered;
});
return filteredData;
};
console.log(f(dataList, userPassedFilter));
A shorter version:
let userPassedFilter = [{"phase":1},{"phase":2}];
let dataList = [{"voltage":35, "phase":2},{"voltage":12, "phase":2},{"voltage":12, "phase":3},{"voltage":35, "phase":1}];
const f = (dataList, userPassedFilter) => {
let filteredData = dataList.filter((data) => {
return userPassedFilter.some(item => Object.entries(item).every(([key, value]) => data[key] === value))
});
return filteredData;
};
console.log(f(dataList, userPassedFilter));
try adding a variable
filteredData = dataList.filter((data) => {
let myvar = true;
for(let item of userPassedFilter){
for(let key in item){
if(data[key] === undefined || data[key] !== item[key])
myvar = false
}
}
return myvar
})
try adding a variable to hold the values:
let userPassedFilter = [{
"phase": 1
}, {
"phase": 2
}]
let dataList = [{
"voltage": 35,
"phase": 2
},
{
"voltage": 12,
"phase": 2
},
{
"voltage": 12,
"phase": 3
},
{
"voltage": 35,
"phase": 1
}
]
function filterData(dataList, userPassedFilter) {
let filteredData = [];
filteredData = dataList.filter((data) => {
var throwVar = 0 //add this variable to hold the value
for (let item of userPassedFilter) {
for (let key in item) {
if (data[key] === undefined || data[key] !== item[key]) {
throwVar = 0
} else {
return true
}
}
}
if (throwVar == 0) {
return false
}
})
return filteredData
}
console.log(filterData(dataList, userPassedFilter))
I'm trying to parse a nested object of the format:
const obj = {
"and": [
{
"!=": [
{
"var": "name"
},
"name1"
]
},
{
"in": [
{
"var": "hobbies"
},
"jogging, video games"
]
}
]
};
I created a function to parse it:
const isOperatorCode(code) => {
const allowedOperatorCodes = ['in', '<=', '!=', '=='];
return allowedOperatorCodes.includes(code);
}
const parseObject = (arg, result={}) => {
if (arg === undefined || arg === null) return arg;
if (arg.constructor === Object && Object.keys(arg).length) {
for (const key in arg) {
const value = arg[key];
if (key === 'and' || key === 'or' || key === '!') {
result.operator = key === '!' ? 'not' : key;
result.operands = [];
return parseObject(value, result);
}
if (isOperatorCode(key)) {
const newItem = {
operator: key,
attribute: value[0].var,
value: value[1]
}
result.operands.push(newItem);
}
}
}
if(Array.isArray(arg) && arg.length) {
for (const k of arg) {
return parseObject(k, result);
}
}
return result;
}
I got this result when I executed the function:
{"operator":"and","operands":[{"operator":"!=","attribute":"name","value":"name1"}]}
it should be:
{"operator":"and","operands":[{"operator":"!=","attribute":"name","value":"name1"}, {"operator":"in","attribute":"hobbies","value":"sport, video games"}]}
I know that the array does not keep the trace of the elements to continue looping through the different items. Any idea or suggestions to keep the track of the array elements and loop on them all?
If you return only at the very-end, you will get your expected result.
const obj = {
"and": [{
"!=": [{
"var": "name"
}, "name1"]
}, {
"in": [{
"var": "hobbies"
}, "jogging, video games"]
}]
};
const isOperatorCode = (code) => {
const allowedOperatorCodes = ['in', '<=', '!=', '=='];
return allowedOperatorCodes.includes(code);
}
const parseObject = (arg, result = {}) => {
if (arg === undefined || arg === null) return arg;
if (arg.constructor === Object && Object.keys(arg).length) {
for (const key in arg) {
const value = arg[key];
if (key === 'and' || key === 'or' || key === '!') {
result.operator = key === '!' ? 'not' : key;
result.operands = [];
parseObject(value, result);
}
if (isOperatorCode(key)) {
const newItem = {
operator: key,
attribute: value[0].var,
value: value[1]
}
result.operands.push(newItem);
}
}
}
if (Array.isArray(arg) && arg.length) {
for (const k of arg) {
parseObject(k, result);
}
}
return result;
}
console.log(parseObject(obj));
.as-console-wrapper { top: 0; max-height: 100% !important; }
return parseObject(k, result); stops execution of the loop so you're only going to get the first item.
for (const k of arg) {
return parseObject(k, result); // return breaks out of the loop. Only processes the first item.
}
Perhaps this would make more sense?
return args.map(k => parseObject(k, result)); // process all entries, return an array.
You could take a recursive approach with look at objects with var as key.
const
convert = object => {
if (!object || typeof object !== 'object') return object;
const [operator, values] = Object.entries(object)[0];
return values[0] && typeof values[0] === 'object' && 'var' in values[0]
? { operator, attribute: values[0].var, value: values[1] }
: { operator, operands: values.map(convert) };
},
object = { and: [{ "!=": [{ var: "name" }, "name1"] }, { in: [{ var: "hobbies" }, "jogging, video games"] }] },
result = convert(object);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I found myself having to process a string like:
foo=bar&foo1=foo%3Dbar%26foo2%3Dfoo%253Dbar
Into:
{
"foo": "bar",
"foo1": {
"foo": "bar",
"foo2": {
"foo": "bar"
}
}
}
A real input example.
My best attempt is:
function parse(input) {
try {
const parsed = JSON.parse(input);
return parseJSON(parsed);
} catch (err) {
const decodedInput = decodeURIComponent(input);
if (input.includes("&") && input.includes("=")) {
return input.split("&").reduce((json, part) => {
const [key, value] = part.split("=");
const decodedValue = decodeURIComponent(value);
return { ...json, [key]: parsePrimitive(decodedValue) };
}, {});
}
return decodedInput;
}
}
function parsePrimitive(input) {
if (!isNaN(input)) {
return Number(input);
}
if (input === "true" || input === "false") {
return input === "true";
}
return parse(input);
}
function parseJSON(input) {
return Object.entries(input).reduce((json, [key, value]) => {
let object = {};
if (typeof value === "object") {
if (Array.isArray(value)) {
object[key] = value;
} else {
object[key] = parseJSON(value);
}
} else {
const decodedValue = decodeURIComponent(value);
if (decodedValue.includes("&") && decodedValue.includes("=")) {
object[key] = parse(decodedValue);
} else {
object[key] = parsePrimitive(decodedValue);
}
}
return { ...json, ...object };
}, {});
}
If you try to run it, you're supposed to call parse(input)
However, it does fail for certain inputs
How can I make the perfect recursive algorithm for this kind of problem?
Thanks!
You could take a recursive approach by checking the encoded = sign.
const getValues = string => string.split('&')
.reduce((r, pair) => {
let [key, value] = pair.split('=');
value = decodeURIComponent(value);
r[key] = value.includes('=')
? getValues(value)
: value;
return r;
}, {});
console.log(getValues('foo=bar&foo1=foo%3Dbar%26foo2%3Dfoo%253Dbar'));
This seems to do it for your simple example and your more complex one (now updated to handle numbers and booleans):
const parse = (query) =>
query .startsWith ('{')
? JSON .parse (query)
: query .includes ('&') || query .includes ('=')
? Object .fromEntries (
query .split ('&')
.map (p => p .split ('='))
.map (([k, v]) => [k, parse (decodeURIComponent (v))])
)
: query .includes (',')
? query .split (',') .filter (Boolean) .map (parse)
: isFinite (query)
? Number (query)
: query .toLowerCase () == "true" || query .toLowerCase () == "false"
? query .toLowerCase () == "true"
: // else
query
const q = 'foo=bar&foo1=foo%3Dbar%26foo2%3Dfoo%253Dbar'
console .log (parse(q))
console.log('fetching larger example...')
fetch ('https://gist.githubusercontent.com/avi12/cd1d6728445608d64475809a8ddccc9c/raw/030974baed3eaadb26d9378979b83b1d30a265a3/url-input-example.txt')
.then (res => res .text ())
.then (parse)
.then (console .log)
.as-console-wrapper {max-height: 100% !important; top: 0}
There are two parts that deserve attention.
First, this makes an assumption about commas: that they represent a separation between elements of an array. And, further, it assumes that empty strings aren't intended, turning
watermark=%2Chttps%3A%2F%2Fs.ytimg.com%2Fyts%2Fimg%2Fwatermark%2Fyoutube_watermark-vflHX6b6E.png
%2Chttps%3A%2F%2Fs.ytimg.com%2Fyts%2Fimg%2Fwatermark%2Fyoutube_hd_watermark-vflAzLcD6.png
into this:
watermark: [
"https://s.ytimg.com/yts/img/watermark/youtube_watermark-vflHX6b6E.png",
"https://s.ytimg.com/yts/img/watermark/youtube_hd_watermark-vflAzLcD6.png"
]
The original starts with an encoded comma (%2C), which would lead to an initial empty string, so we use .filter (Boolean) to remove it.
Second, the test for a string representing JSON is very naïve, only doing .startsWith ('{'). You can replace this with whatever you need, but it leads to a question of intentions. I'm not sure we can write this entirely generically in this manner.
Still, I think it's close. And the code is fairly clean.
I do have to wonder why, however. This much data is going to run into various url size limits. At this point, wouldn't putting this into a request body rather than url parameters make much more sense?
Node.js comes with a built-in "querystring" npm package utility, but here I used a better one called "qs". You can specify delimiters in an array instead of only using one with the former.
If you want to use the built-in "querystring" package, you need to remove the delimiter array when calling parse and do a check on the string to see what delimiter is used - the sample file you gave use a few different ones.
So try this:
const qs = require("qs");
let params = `foo=bar&foo1=foo%3Dbar%26foo2%3Dfoo%253Dbar`;
const isObject = (param) => {
try {
let testProp = JSON.parse(param);
if (typeof testProp === "object" && testProp !== null) {
return true;
}
return false;
} catch (e) {
return false;
}
};
const isURL = (value) => {
try {
new URL(value);
} catch (e) {
return false;
}
return true;
};
const isQueryString = (value) => {
if (/[/&=]/.test(value) && !isURL(value)) {
return true;
} else {
return false;
}
};
const parseData = (data, parsed = false) => {
if (isQueryString(data) && !parsed) {
return parseData(qs.parse(data, { delimiter: /[;,/&]/ }), true);
} else if (isObject(data) || parsed) {
for (let propertyName in data) {
if (isObject(data[propertyName])) {
data[propertyName] = parseData(JSON.parse(data[propertyName]), true);
} else {
data[propertyName] = parseData(data[propertyName]);
}
}
return data;
} else {
return data;
}
};
let s = parseData(params);
console.log(JSON.stringify(s, null, 2));
I reworked the algorithm using Object.fromEntries(new URLSearchParams()).
function parse(query) {
try {
return JSON.parse(query);
} catch {
if (!isNaN(query)) {
return Number(query);
}
if (typeof query !== "string") {
const obj = {};
for (const queryKey in query) {
if (query.hasOwnProperty(queryKey)) {
obj[queryKey] = parse(query[queryKey]);
}
}
return obj;
}
if (!query) {
return "";
}
if (query.toLowerCase().match(/^(true|false)$/)) {
return query.toLowerCase() === "true";
}
const object = Object.fromEntries(new URLSearchParams(query));
const values = Object.values(object);
if (values.length === 1 && values[0] === "") {
return query;
}
return parse(object);
}
}
const q = 'foo=bar&foo1=foo%3Dbar%26foo2%3Dfoo%253Dbar';
console.log(parse(q));
console.log('fetching larger example...');
fetch('https://gist.githubusercontent.com/avi12/cd1d6728445608d64475809a8ddccc9c/raw/030974baed3eaadb26d9378979b83b1d30a265a3/url-input-example.txt')
.then(response => response.text())
.then(parse)
.then(console.log);
.as-console-wrapper { max-height: 100% !important; top: 0; }
my function i am trying to unit test is given as
NOTE : mypayload= payload.Items i will give a JSON reference at the end of the question
function setIdtoDeviceIdIfEmptyAndSetNullIfFieldEmpty(mypayload) {
let _res = mypayload.map(ele => {
if (ele.id == null) {
ele.id = ele.deviceId
}
if (ele.id == null && ele.deviceId == null) {
ele.id = '***Both id and device id is null***'
}
if (ele.ssid == null) {
ele.ssid = '***ssid is null***'
}
if (ele.deviceName == null) {
ele.deviceName = '**deviceName is null**'
}
return ele;
})
return _res
}
I am trying to test the most basic part that is the first if loop ele.id==null
I have tried :
describe('Test setIdtoDeviceIdIfEmptyAndSetNullIfFieldEmpty', () => {
test('testing null', () => {
index.setIdtoDeviceIdIfEmptyAndSetNullIfFieldEmpty(mypayload)
mypayload.map(ele => {
if (ele.id == null) {
expect(ele.id).toEqual(ele.deviceId)
}
})
})
})
However i cannot get this test case to fail for ex if i enter expect(ele.id).toEqual("random") it still passes when i am expecting it to fail,Clearly i am doing something wrong
Reference
const payload = {"Items":[
{
"id" :"mycompany-00b12396",
"ssid": "home",
"deviceName": "mydevice",
"time": "0.753235299 Hours"
},
{
"ssid": "office",
"deviceName": "thisdevice",
"time": "12.52115515 Hours",
"deviceId":"mycompany-0031f13"
}
]}
You should test the result of the function like this:
describe('Test setIdtoDeviceIdIfEmptyAndSetNullIfFieldEmpty', () => {
test('testing null', () => {
const result = index.setIdtoDeviceIdIfEmptyAndSetNullIfFieldEmpty(mypayload)
result.map(ele => {
if (!ele.id) {
expect(ele.id).toEqual(ele.deviceId)
}
})
})
})
I have been trying and this worked for me hope it helps someone in the future
describe('Test setIdtoDeviceIdIfEmptyAndSetNullIfFieldEmpty', () => {
test('testing null', () => {
const result = index.setIdtoDeviceIdIfEmptyAndSetNullIfFieldEmpty(mypayload)
let check = result.find(el => el.deviceId === "mycompany-00a412f");
expect(check.id).toEqual(check.deviceId)
})
})