I have a search input in my angular application that should compare the input data with different object properties
<div class="forms">
<div class="search-wrapper">
<input
class="search"
[ngClass]="{'searching': searching}"
type="text"
(input)="changeSearch($event.target.value)"
/>
<label class="">
<span>Rechercher</span>
</label>
</div>
</div>
the logic I use is as follows
public changeSearch(searchTerm: string) {
this.searching = !!searchTerm;
if (!this.searching) {
this.orders.forEach(order => {
order.show = true;
});
return;
}
const extendSearchIn = ['order_number', 'transaction.item.product.name'];
this.orders.forEach(order => {
order.show = true;
extendSearchIn.forEach(property => {
this.searchByProperty(order, property, searchTerm);
});
});
}
public searchByProperty(order, property, searchTerm) {
const prop = this.getSearchProperty(order, property);
if (prop === undefined) { return false; }
return (<String>prop.toLowerCase()).startsWith(searchTerm.toLowerCase());
}
public getSearchProperty(item: object, property: string) {
let itemCopy = Object.assign({}, item);
let result: any;
const props = property.split('.');
props.forEach(prop => {
if (itemCopy !== undefined) {
itemCopy = itemCopy[prop];
}
});
result = itemCopy !== undefined ? itemCopy : result;
return result;
}
and the structure of each object 'order' is like the following
{
"functional_id": "202006101058160012400000SD4AYAA1",
"transactions": [
{
"quantity": 2,
"price": 140,
"item": {
"name": "Carton",
"description": "+ 2 recharges",
"product": {
"name": "Coffret rouge"
}
},
"amount": 280
},
{
"quantity": 2,
"price": 140,
"item": {
"name": "10 coffrets",
"description": "+ 2 recharges",
"product": {
"name": "Coffret gris"
}
},
"amount": 280
},
{
"quantity": 2,
"price": 60,
"item": {
"name": "PACK N°1 comprenant :",
"description": "6 modèles",
"product": {
"name": "AfuBOX",
"description": "60,8 x 39,5 x 16,5 cm"
}
},
"amount": 120
}
],
"show": true,
"date": "10/06/2020",
"order_number": "105816",
"overallAmount": 680
}
you would need to set the 'show' property to false so that those that don't comply with what was entered in the search field would be hidden
Someone to make me see where my mistake is.
Thank you in advance
I have simplified the logic with a forEach and checking if the value I receive from the input contains any of the search criteria I wanted to apply.
I hope that this will help you to explain if you find yourself in a similar situation.
public changeSearch(searchTerm: string) {
this.searching = !!searchTerm;
if (!this.searching) {
this.orders.forEach(order => {
order.show = true;
});
return;
}
this.orders.forEach(order => {
order.show = true;
this.searchByProperty(order, searchTerm);
});
}
public searchByProperty(order, searchTerm) {
const id = (order.order_number + '');
const amount = (order.overallAmount + '');
order.transactions.forEach(items => {
const title = items.item.product.name.toLowerCase();
if (title.includes(searchTerm) || id.includes(searchTerm) || amount.includes(searchTerm)) {
order.show = true;
} else {
order.show = false;
}
});
}
Related
Example JSON file:
[
{
"discordId": "9273927302020",
"characters": [
{
"name": "Rare_Character",
"value": 1
},
{
"name": "Ultra_Rare_Character",
"value": 1
}
]
}
]
Let's just say for example I ran this simple gacha and got 4 characters:
let i = 1
var picks = []
while(i <= 4){
const { pick } = gacha.simple(alpha)
picks.push(pick)
i++
}
Now, picks has an array like this:
[
{
"name": "Common_Character"
},
{
"name": "Ultra_Rare_Character"
},
{
"name": "Common_Character"
},
{
"name": "Rare_Character"
}
]
How do I increment the value in My Example JSON file based on the name from what I got in my gacha results picks while ignoring the Common_Character and only passing those Rare and Ultra_Rare ones?
I've tried filtering them like this:
var filter = picks.filter(t => t.name === 'Rare_Character' || t.name === 'Ultra_Rare_Character')
Now I don't know how to increase those values in my JSON file and what if in the gacha results I got two Rare_Characters or Ultra_Rare_Character
I'm using fs to read my JSON file but I just don't know the logic to increase values
const src = [
{
"discordId": "9273927302020",
"characters": [
{
"name": "Rare_Character",
"value": 1
},
{
"name": "Ultra_Rare_Character",
"value": 1
}
]
}
];
const gacha = [
{
"name": "Common_Character"
},
{
"name": "Ultra_Rare_Character"
},
{
"name": "Common_Character"
},
{
"name": "Rare_Character"
}
];
const updateValues = (src, gacha) => {
const gachaSums = gacha.reduce((collector, current) => {
collector[current.name] = (collector[current.name] | 0) + 1;
return collector;
}, {});
src.characters.forEach(srcChar => {
gachaSums[srcChar.name] = srcChar.value + (gachaSums[srcChar.name] | 0);
});
src.characters = Object.entries(gachaSums).map(([key, value]) =>
({ name: key, value: value })
);
return src;
}
console.log(updateValues(src[0], gacha));
Maybe this version could help
I have a dynamic JSON structure ex:
{
"video": {
"width": 1920,
"height": 1080,
"video_codec": "H264",
"CBR": "4337025",
"frame_rate": {
"numerator": 25,
"denominator": 1
},
"specified": {
"numerator": 1,
"denominator": 1
},
"gop": {
"length": 50,
"reference_frames": 3,
"sub_gop": "StaticType"
},
"codec_details": {
"profile": "Main",
"level": "Level4",
"entropy_encoding": "CABAC",
"video_output": "AVC1"
}
}
}
I want to transform it to tree node
export class TrackDetailsNode {
key: string;
value: string;
children?: TrackDetailsNode[];
}
Output Example:
{
"key": "video",
"children": [
{
"key": "width",
"value": "1920"
},
{
"key": "frameRate",
"children": [
{
"key": "numerator",
"value": "60"
},
{
"key": "denominator",
"value": "1"
}
]
}
]
}
I need it in a recursive way. I want to build a tree from the provided JSON.
Tried multiple solution but is taking a long time to parse.
var obj = {
"video": {
"width": 1920,
"height": 1080,
"video_codec": "H264",
"CBR": "4337025",
"frame_rate": {
"numerator": 25,
"denominator": 1
},
"specified": {
"numerator": 1,
"denominator": 1
},
"gop": {
"length": 50,
"reference_frames": 3,
"sub_gop": "StaticType"
},
"codec_details": {
"profile": "Main",
"level": "Level4",
"entropy_encoding": "CABAC",
"video_output": "AVC1"
}
}
};
function transform(data, output, children) {
Object.keys(data).forEach((key) => {
let value = data[key];
if (children) {
if (typeof value === 'object') {
const child = [];
children.push({
'key': key,
children: child
});
transform(value, undefined, child)
} else {
children.push({
'key': key,
value
})
}
} else {
if (typeof value === 'object') {
output.key = key;
output.children = [];
transform(value, undefined, output.children);
} else {
output.key = key;
output.value = value;
}
}
});
return output;
}
console.log(transform(obj, {}));
const INPUT = {
"video": {
"width": 1920,
"height": 1080,
"video_codec": "H264",
"CBR": "4337025",
"frame_rate": {
"numerator": 25,
"denominator": 1
},
"specified": {
"numerator": 1,
"denominator": 1
},
"gop": {
"length": 50,
"reference_frames": 3,
"sub_gop": "StaticType"
},
"codec_details": {
"profile": "Main",
"level": "Level4",
"entropy_encoding": "CABAC",
"video_output": "AVC1"
}
}
};
const isObject = (obj) => obj !== null && typeof obj === 'object';
const Transform = (input, isChildren) => {
const output = isChildren ? [] : {};
if (input && isObject(input)) {
Object.keys(input).forEach(key => {
const value = input[key];
const val = isObject(value) ? Transform(value, true) : value;
const valueKey = isObject(value) ? "children" : "value";
if (isChildren) {
output.push({
"key": key,
[valueKey]: val
});
} else {
output.key = key;
output[valueKey] = val;
}
})
}
return output;
}
const result = Transform(INPUT);
console.log(JSON.stringify(result, null, 2));
I get an input like this:
input 1:
{
"name": "Ben",
"description": "Ben",
"attributes": [
{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
input 2
{
"name": "Ice",
"description": "Ice",
"attributes": [
{
"type": "Background",
"value": "Green"
},
{
"type": "Hair-color",
"value": "White"
}
]
}
input 3
{
"name": "Itay",
"description": "Itay",
"attributes": [
{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
What I want to do is count the amount of each type of background and each type of hair-color appearing.
(These are sample examples and in reality there are more types and different values)
Let's say in these examples we have 2 objects that have a background as default then I want to have a count of that like so:
export interface TraitCount {
value: string,
count: number
}
export interface CountOfEachAttribute {
trait_type: string,
trait_count: traitCount[] | null,
total_variations: number
}
I want the most effective code because there are other aspects to the code, in addition it will run on 5-10k queries not just three, so needs
to run in good times too :D
(It's similar to my other question done with python but now I need it in js also)
Atm it's something like this:
(Apart of a much bigger code so keep that in mind)
setInitalCountOfAllAttribute( state, { payload }: PayloadAction<CountOfEachAttribute[] | null> ) {
if (payload === null) {
state.countOfAllAttribute = null;
} else {
state.countOfAllAttribute = payload;
}
},
setCountOfAllAttribute(state, { payload }: PayloadAction<Attribute>) {
if (state.countOfAllAttribute !== null) {
state.countOfAllAttribute.map(
(countOfEachAttribute: CountOfEachAttribute) => {
// Find the trait type
if (countOfEachAttribute.trait_type === payload.trait_type) {
// initiate the trait count array to store all the trait values and add first trait value
if (countOfEachAttribute.trait_count === null) {
const new_trait_count = { value: payload.value, count: 1 };
countOfEachAttribute.trait_count = [new_trait_count];
countOfEachAttribute.total_variations++;
}
// Trait array already existed.
else {
// Check if value already present or not
const checkValue = (obj: any) => obj.value === String(payload.value);
const isPresent = countOfEachAttribute.trait_count.some(checkValue)
const isPresent2 = countOfEachAttribute.trait_count.find((elem: any) => elem.value === String(payload.value))
// Value matched, increase its count by one
if (isPresent2) {
countOfEachAttribute.trait_count &&
countOfEachAttribute.trait_count.map((trait) => {
if (trait.value === payload.value) {
trait.count++;
}
});
}
// Value doesn't match, add a new entry and increase the count of variations by one
else {
const new_trait_count = { value: payload.value, count: 1 };
countOfEachAttribute.trait_count = [
...countOfEachAttribute.trait_count,
new_trait_count,
];
countOfEachAttribute.total_variations++;
}
}
}
}
);
}
},
You can merge all arrays and use Array.reduce.
const input1 = {
"name": "Ben",
"description": "Ben",
"attributes": [{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
const input2 = {
"name": "Ice",
"description": "Ice",
"attributes": [{
"type": "Background",
"value": "Green"
},
{
"type": "Hair-color",
"value": "White"
}
]
}
const input3 = {
"name": "Itay",
"description": "Itay",
"attributes": [{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
const mergedInput = [input1, input2, input3];
const result = mergedInput.reduce((acc, item) => {
item.attributes.forEach(attrItem => {
const existType = acc.find(e => e.trait_type == attrItem.type);
if (existType) {
var existAttr = existType.trait_count.find(e => e.value == attrItem.value);
if (existAttr) {
existAttr.count++;
} else {
existType.trait_count.push({
value: attrItem.value,
count: 1
});
existType.total_variations++;
}
} else {
acc.push({
trait_type: attrItem.type,
trait_count: [{
value: attrItem.value,
count: 1
}],
total_variations: 1
})
}
});
return acc;
}, []);
console.log(result);
I suggest instead of creating an array for trait_count to make it an object so you don't have to iterate over it whenever you are adding a new attribute. In the snippet below I'm using the value of the attribute as a sort of hash that allows the access to the given property without having to call the Array.prototype.find function
const input1 = {"name":"Ben","description":"Ben","attributes":[{"type":"Background","value":"Default"},{"type":"Hair-color","value":"Brown"}]};
const input2 = {"name":"Ice","description":"Ice","attributes":[{"type":"Background","value":"Green"},{"type":"Hair-color","value":"White"}]};
const input3 = {"name":"Itay","description":"Itay","attributes":[{"type":"Background","value":"Default"},{"type":"Hair-color","value":"Brown"}]};
function countAtributes(input, totalCounts={}) {
input.attributes.forEach((attribute) => {
if (!totalCounts[attribute.type])
totalCounts[attribute.type] = {trait_type: attribute.type, trait_count: {}, total_variations: 0};
if (!totalCounts[attribute.type].trait_count[attribute.value]) {
totalCounts[attribute.type].trait_count[attribute.value] = {value: attribute.value, count: 1};
totalCounts[attribute.type].total_variations+=1;
}
else totalCounts[attribute.type].trait_count[attribute.value].count +=1;
})
}
const totalCounts = {};
countAtributes(input1, totalCounts);
countAtributes(input2, totalCounts);
countAtributes(input3, totalCounts);
console.log(totalCounts);
It could be turned into the array afterwards with Object.values if necessary
I believe it is a much better approach to what you had before as you don't have to iterate over the tables of trait_counts. In theory it should significantly reduce the time taken. Iterating over the array and checking a condition each time is much slower than key lookup in Javascript object
Have inherited a Node.js app that needs some maintenance and its not my strong point.
We are parsing XML using fast-xml-parser which works really well with most of our inputs. However we have some inputs that are an extra level deep, and we need to flatten the output to all be same level.
The Input: where Price value is the extra level deep
<products capture-installed="true">
<script/>
<script/>
<script/>
<product>
<pid>8</pid>
<modelno>6273033</modelno>
<name>
<![CDATA[ Big Red Truck ]]>
</name>
<category>
<![CDATA[ Toys]]>
</category>
<currency>USD</currency>
<price>
<actualprice>19.20</actualprice>
</price>
</product>
When we flatten it with existing code we get:
"product": {
"pid": "8",
"modelno": "6273033",
"name": "Big Red Truck",
"category": "Toys",
"currency": "USD",
"price": {
"actualprice": "19.20"
}
But what we need is something like:
"product": {
"pid": "8",
"modelno": "6273033",
"name": "Big Red Truck",
"category": "Toys",
"currency": "USD",
"price-actualprice": "19.20"
}
The current Code:
const parse = require("fast-xml-parser");
const options = {
ignoreAttributes : true,
ignoreNameSpace : false,
parseNodeValue : false,
tagValueProcessor : a => {
if(Array.isArray(a)){
return a.join(',');
}
return a;
}
};
const flatten = (data) => {
return data.map(row => {
const fieldNames = Object.keys(row);
for (const fieldName of fieldNames) {
if(Array.isArray(row[fieldName])){
row[fieldName] = row[fieldName].join(',');
}
if(typeof row[fieldName] === 'object'){
row[fieldName] = JSON.stringify(row[fieldName]);
}
}
return row;
});
};
function findTheArray(o) {
if(Array.isArray(o)){
return o;
}
var result, p;
for (p in o) {
if( o.hasOwnProperty(p) && typeof o[p] === 'object' ) {
result = findTheArray(o[p]);
if(result){
return result;
}
}
}
return result;
}
module.exports = function parseData(data) {
return new Promise((resolve, reject) => {
try {
const isValid = parse.validate(data);
if (isValid === true) {
const pData = parse.parse(data, options);
const array = findTheArray(pData);
if(array){
resolve(flatten(array));
} else {
reject('Can\'t find any goodies!');
}
} else {
reject(isValid.err);
}
} catch (err) {
reject(err);
}
});
};
I've worked on the this area of the code but haven't been able to get any success:
if(typeof row[fieldName] === 'object'){
row[fieldName] = JSON.stringify(row[fieldName])
Ideas?
thanks
In the recent version of FXP, this is how you can do;
const options = {
ignoreAttributes: true,
stopNodes: [
"products.product.price"
],
tagValueProcessor: (tagName, tagValue, jPath, hasAttributes, isLeafNode) => {
if (jPath === 'products.product.price') {
return /([0-9]+\.[0-9]+)/.exec(tagValue)[1]
}
},
// preserveOrder: true,
};
const parser = new XMLParser(options);
let result = parser.parse(XMLdata);
Output
"product": {
"pid": "8",
"modelno": "6273033",
"name": "Big Red Truck",
"category": "Toys",
"currency": "USD",
"price": "19.20"
}
However, tag name can't be changed.
I need to go through a list of objects to find the element and add a new element to the root, I can scroll through the list and find the element, but I can not add to the correct level
var data = [
{
"id": 1
},
{
"id": 2
},
{
"id": 3
},
{
"id": 4,
"children": [
{
"id": 6
},
{
"id": 7
}
]
},
{
"id": 5
}
];
function findById(data, id, element) {
function iter(a) {
if (a.id === id) {
a.push(element); // ERROR
result = a;
return true;
}
return Array.isArray(a.children) && a.children.some(iter);
}
var result;
data.some(iter);
return result
}
var element = {
"children": [{"id": 6}]
};
findById(data, 5, element);
document.write('<pre>' + JSON.stringify(data, 0, 4) + '</pre>');
https://jsfiddle.net/4nrsccnu/
Use Object.assign to merge the properties from the element object to the current object of the iteration.
var data = [
{
"id": 1
},
{
"id": 2
},
{
"id": 3
},
{
"id": 4,
"children": [
{
"id": 6
},
{
"id": 7
}
]
},
{
"id": 5
}
];
function findById(data, id, element) {
function iter(a) {
if (a.id === id) {
Object.assign(a, element);
result = a;
return true;
}
return Array.isArray(a.children) && a.children.some(iter);
}
var result;
data.some(iter);
return result
}
var element = {
"children": [{"id": 6}]
};
findById(data, 5, element);
console.log(data);
You cannot push, because a is an object. However, you can simply add the desired property (in your case, children), and then assign it a value (in your case, the element).
var data = [
{
"id": 1
},
{
"id": 2
},
{
"id": 3
},
{
"id": 4,
"children": [
{
"id": 6
},
{
"id": 7
}
]
},
{
"id": 5
}
];
function findById(data, id, element) {
function iter(a) {
if (a.id === id) {
a.children = element; // Add property 'children'
result = a;
return true;
}
return Array.isArray(a.children) && a.children.some(iter);
}
var result;
data.some(iter);
return result
}
// remove property name from element, as that is being added in the function
var element = [
{"id": 6}
];
findById(data, 5, element);
document.write('<pre>' + JSON.stringify(data, 0, 4) + '</pre>');
The push function is used for arrays, not to object. Check for details https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push.
If you want to add a single keypair to your object, you can use Object.keys and Object.values. Like this:
function findById(data, id, element) {
function iter(a) {
if (a.id === id) {
var key = Object.keys(element)[0];
var value = Object.values(element)[0];
a[key] = value;
result = a;
return true;
}
return Array.isArray(a.children) && a.children.some(iter);
}
var result;
data.some(iter);
return result;
}