I am modifying JSON files/Javascript object using Pinch library: https://github.com/Baggz/Pinch
In this example, pinch() with regex parameter /id/ modifies ALL the id values to 321.
What I want to do is be able to change the value of all ids but only for a specific "Requestor", based on a parameter (requestorToChange). Let's say "RequestorX". How do I write the regex for it?
var sample = {
"RequestorX":
[{
user: {
id: '123'
},
request: {
id: '456'
},
book: {
id: '789'
}
}],
"RequestorY":
[{
user: {
id: '111'
},
request: {
id: '222'
},
book: {
id: '333'
}
}]
};
const requestorToChange = 'RequestorX'
pinch(sample, /id/, function(path, key, value) {
return '321';
});
console.log(JSON.stringify(sample))
I know that one option is to just do:
pinch(sample['RequestorX'], /id/, function(path, key, value) {
return '321';
});
But I need to be able to do it via the regex field since in reality, I will be manipulating deeply nested JSON files.
The explanation on the GitHub page is a bit sparse, but basically you start with the dot notation replacement style and replace the variable parts with regex patterns whilst adhering to the rules of JS regular expressions.
So, in your case, a dot notation pattern 'RequestorX[0].user.id'
becomes a regex like /RequestorX\[0\]\.(user|request|book)\.id/
Somes notes:
first we have to escape the square brackets because those have special meaning in regex
next, we also have to escape the . to use it as dot notation symbol
we use a group with alternations to replace the id on all of them
I tried to use new RegExp instead of the /../ notation to create a regex Object to add your constant to the pattern, unfortunately only the first item is replaced in this case, probably a bug...
pinch(sample, new RegExp(requestorToChange +'\[0\]\.(user|request|book)\.id'), '321');
Sample code (the SO code snippet outputs some extra gibberish about RequestorY, ignore it)
var sample = {
"RequestorX": [
{
"user": {
"id": "123"
},
"request": {
"id": "456"
},
"book": {
"id": "789"
}
}
],
"RequestorY": [
{
"user": {
"id": "123"
},
"request": {
"id": "456"
},
"book": {
"id": "789"
}
}
]
}
const requestorToChange = 'RequestorX';
//var result = pinch(sample, 'RequestorX[0].user.id', '321'); //replace the user id of RequestorX with dot notation
var result = pinch(sample, /RequestorX\[0\]\.(user|request|book)\.id/, '321');
//var result = pinch(sample, new RegExp(requestorToChange +'\[0\]\.(user|request|book)\.id'), '321');
console.log(JSON.stringify(result));
<script src="https://cdn.jsdelivr.net/npm/pinch#0.1.3/src/pinch.js"></script>
Alas, JSON + regex sounds a bit weird but the tool looks legit.
Related
I am trying to filter some articles from a graphql response, by articleTag. Se my structure below:
{
"id": "41744081",
"articleTitle": "text",
"articleContent": "text",
"categoryName": { "categoryName": "Company", "id": "38775744" },
"articleTags": [
{ "articleTag": "event", "id": "37056861" },
{ "articleTag": "car", "id": "37052481" },
]
},
{
"id": "41754317",
"articleTitle": "text",
"articleContent": "text",
"categoryName": { "categoryName": "Sales and Martketing", "id": "38775763" },
"articleTags": [{ "articleTag": "technology", "id": "37056753" }]
},
...
But when applying my function:
notificationFiltered () {
var articleResponse = this.response.allArticles;
var routeParam = this.$route.params.tagName; //contains the id of the tag
const filteredTag = articleResponse.filter((item) => {
return (item.articleTags.indexOf(routeParam) >= 0);
});
console.log(filteredTag);
},
When I'm "console.log" the result I get only a "[]". Not sure if is related with the way of query is being render, in the API I get the same formation but with this slightly difference
{
"data": {
"allArticles": [... the specify structure above]
}
}
while printing that with vue {{response.allArticles}} I just get the first structure, I think it shouldn't matter?
Thanks in advance for the advice
You won't be able to use indexOf for array of objects to find a matching object - only strict equality is needed, and that's hard to get in the reference land. Consider this:
const objs = [
{ foo: 'bar' },
{ foo: 'baz' },
{ foo: 'foo' } // whatever
];
const needle = { foo: 'baz' };
objs.indexOf(needle);
// -1
What? Yes, there's an object looking exactly like needle in that array - but it's a different object:
objs[1] === needle; // false
That's why indexOf just goes past that one - and gives out -1, a "not found" result.
What you should be able to use in this case is findIndex. Still you need to build the predicate to have a match. For example:
const objs = [
{ foo: 'bar' },
{ foo: 'baz' },
{ foo: 'foo' }
];
const needle = { foo: 'baz' };
objs.findIndex(el => JSON.stringify(el) === JSON.stringify(needle));
// 1
In this example, comparing results of JSON.stringify in the predicate function is a poor man's _.isEqual - just to illustrate the concept. What you should consider actually using in your code is either _.isEqual itself, or similar function available in toolkit of your choice.
Alternatively, you can just check for specific fields' values:
objs.findIndex(el => el.foo === needle.foo); // still 1
This will apparently find objects even if their other properties do not match though.
I am having one object like below and now I need to remove the double quotes if the any of the value is either Number or null. Other than these three cases, double quotes should be present.
All these values are fetching from textbox, If I provide value as 1234, its getting added as "1234".
so, here itself I am facing this issue. How to avoid this? Please advise
{
"fruits":[{
"name":"apple",
"count":"4",
"filter":{
"unique":[{
"attribute":"isFruit",
"identifier":"9876"
}],
"match_attributes":{
"location":"NewZeland",
"subname":"null"
},
"match_expressions":[{
"attribute":"value1",
"operator":"In",
"values":["test_value","4567","value7"]
},
{
"attribute_name":"isvegetable",
"operator":"In",
"values":["15678"]
}]
}
}]
}
My expected output should be like below,
{
"fruits":[{
"name":"apple",
"count":4,
"filter":{
"unique":[{
"attribute":"isFruit",
"identifier": 9876
}],
"match_attributes":{
"location":"NewZeland",
"subname":null
},
"match_expressions":[{
"attribute":"value1",
"operator":"In",
"values":["test_value",4567,"value7"]
},
{
"attribute_name":"isvegetable",
"operator":"In",
"values":[15678]
}]
}
}]
}
I tried to remove for single value using
string.replace(/['"]+/g, '');
But is there any way to replace it for an entire object. Any help would be appreciated.
You could create your own parser to handle the special cases words you want to convert. In this case 'true', 'false' and 'null'
As for the numbers you can check if it's a number or not using isNaN then format it.
const input = {
"fruits":[{
"name":"apple",
"count":"4",
"filter":{
"unique":[{
"attribute":"isFruit",
"identifier":"true"
}],
"match_attributes":{
"location":"NewZeland",
"subname":"null"
},
"match_expressions":[{
"attribute":"value1",
"operator":"In",
"values":["test_value","4567","value7"]
},
{
"attribute_name":"isvegetable",
"operator":"In",
"values":["false"]
}]
}
}]
};
const dictionary = ["null", "true", "false"]
const parser = word => {
wordWithoutQuotes = word.substring(1, word.length -1)
if (dictionary.includes(wordWithoutQuotes) || !isNaN(wordWithoutQuotes)) return JSON.parse(word)
return word
}
const output = JSON.stringify(input).replace(/".*?"/g, parser);
console.log(output)
I have 3 different jsons, I need to extrapolate some data from each and create a new json with it. The three jsons have an id identifier in common, a unique identifier, so We could use that as a match since they are actually three different big jsons.
On json one we have "id":"265", on two and three "article_id":"265", so these can be the reference point when we loop.
I never worked with json this way so I wouldn't know how to approach it. I have put jQuery and JS as tags as they're what I know best.
1
{
"id":"265",
"title":"Battle of Gettysburg",
"page_id":"4849",
"language_id":"en",
"original_time":"July 1\u20133, 1863"
}
2
{
"id":"185",
"original_name":"United States",
"country_id":"24",
"article_id":"265"
}
3
{
"id":"73",
"month":"July",
"year":"1863",
"suffix":"",
"article_id":"265"
}
So the end result I am looking for is a single json exactly like this, we take id and title as objects from json 1, then we grab original_name from json two and year object from json three and we'll have:
{
"id":"265",
"title":"Battle of Gettysburg",
"original_name":"United States",
"year":"1863"
}
NOTE
The json above are just examples, in reality they are three huge lists, what I could do (manually), is to join them in order to have a single json.
There is some terminology confusion here; based on your comments you could be asking one of two very different questions. Fortunately one of them is very simple to answer so let's do both.
(I am handwaving past the details of loading json strings into the browser and converting them into javascript objects.)
If you have three objects
...then this is just a matter of plucking out the fields you need individually when constructing an output object:
var in1 = {
"id": "265",
"title": "Battle of Gettysburg",
"page_id": "4849",
"language_id": "en",
"original_time": "July 1\u20133, 1863"
};
var in2 = {
"id": "185",
"original_name": "United States",
"country_id": "24",
"article_id": "265"
}
var in3 = {
"id": "73",
"month": "July",
"year": "1863",
"suffix": "",
"article_id": "265"
}
// construct a new object using the selected fields
// from each object in1, in2, or in3:
var out = {
id: in1.id,
title: in1.title,
original_name: in2.original_name,
year: in3.year
}
console.log(out);
If you have three lists of objects:
...in this case it's a lot more complicated (and a lot more interesting). In this case you would need to match fields from the objects in each list which share the same IDs.
The following is definitely not the most efficient or memory-conserving way to do this; I've spread things out to (hopefully) make it easier to follow what it's doing.
I'm making two assumptions:
within each list, all IDs are unique (meaning you won't have two objects with the same ID in one JSON file)
Every ID will appear in all three lists (meaning you don't need to handle missing fields in output)
/* Again handwaving past loading JSON strings and parsing
them into javascript objects, we'll just start with
three arrays: */
var input1 = [{
"id": "265",
"title": "Battle of Gettysburg",
"page_id": "4849",
"language_id": "en",
"original_time": "July 1\u20133, 1863"
},
{
"id": "1",
"title": "Foo",
"page_id": "123",
"language_id": "en",
"original_time": "July 1\u20133, 1863"
}
];
var input2 = [{
"id": "1",
"original_name": "Bar",
"country_id": "24",
"article_id": "265"
},
{
"id": "265",
"original_name": "United States",
"country_id": "24",
"article_id": "265"
}
]
var input3 = [{
"id": "1",
"month": "July",
"year": "Baz",
"suffix": "",
"article_id": "265"
},
{
"id": "265",
"month": "July",
"year": "1863",
"suffix": "",
"article_id": "265"
}
]
/* It would be much easier to find corresponding IDs
across these arrays if they weren't arrays. We'll
start by converting them into objects keyed by the
item ids: */
var convertArray = function(arr) {
var output = {};
arr.forEach(function(o) {
output[o.id] = o;
});
return output;
}
var obj1 = convertArray(input1);
var obj2 = convertArray(input2);
var obj3 = convertArray(input3);
/* Now if we need to find (say) the object with id "foo", we don't
need to search the whole array, but can just use `obj1["foo"]` or
`obj1.foo`.
The last step is to iterate over the list of IDs and repeatedly
do basically the same thing as in the "if you have three objects"
part above. The only difference is that we need to access the
object with the same ID in each of the input lists: */
var constructOutput = function(in1, in2, in3) {
var output = []; // we'll be outputting a list of objects again.
// step through every ID (assuming in1 contains all of them):
Object.keys(in1).forEach(function(id) {
var obj = {
id: id,
title: in1[id].title,
original_name: in2[id].original_name,
year: in3[id].year
}
output.push(obj);
});
return output;
}
var final = constructOutput(obj1, obj2, obj3)
console.log(final)
Essentially what you have to do is mimic a SQL JOIN using JavaScript objects:
Use JSON.parse() on all three JSON collections to turn them into arrays of objects.
Iterate through JSON 1 objects; for each object...
Iterate through JSON 2 objects, testing if article ID matches the ID from JSON 1 that we are iterating over. Save this object.
Iterate through JSON 3 objects, testing if ID matches the ID of the object we found from JSON 2. Save this object.
After you have all three objects, make a new object literal that contains only the fields you want:
{
Id: obj1.id,
Title: obj1.title,
Original_name: obj2.original_name,
Year: obj3.year
}
Should you want to combine n number of JSON objects, e.g. a list of objects you can take a functional approach and utilise reduce + filter.
const data = [{
"id":"265",
"title":"Battle of Gettysburg",
"page_id":"4849",
"language_id":"en",
"original_time":"July 1\u20133, 1863"
},
{
"id":"185",
"original_name":"United States",
"country_id":"24",
"article_id":"265"
},
{
"id":"73",
"month":"July",
"year":"1863",
"suffix":"",
"article_id":"265"
}];
const final = data.reduce((accu, { id, title }, index, array) => {
// Find any related objects
const matches = array.filter(data => data.article_id === id);
if (matches.length) {
// Flatten them for ease of access. Duplicate keys will override.
const flat = matches.reduce((arr, item) => ({ ...arr, ...item }), [])
// Return new object
return accu.concat({
...flat,
id,
title,
});
}
return accu;
}, []);
console.log(final, '<<')
// Witness
document.getElementById('results').innerHTML = JSON.stringify(final);
<div id="results" style="font-family: Courier; font-size 14px; color: #fff; background: #000; padding: 20px; max-width: 80vw;"></div>
Edited*
Maybe this is what you need?
let arrPages = [{
"id":"265",
"title":"Battle of Gettysburg",
"page_id":"4849",
"language_id":"en",
"original_time":"July 1\u20133, 1863"
}];
let arrArticles = [{
"id":"185",
"original_name":"United States",
"country_id":"24",
"article_id":"265"
},
{
"id":"73",
"month":"July",
"year":"1863",
"suffix":"",
"article_id":"265"
}];
let getResult = (arrInput, arrCompare) => {
let joinedItems = [];
arrInput.forEach(item => {
let newItem = { id: item.id, title: item.title };
arrCompare.forEach(subItem => {
if(subItem.article_id !== undefined && subItem.article_id === item.id){
if(subItem.original_name !== undefined)
newItem.original_name = subItem.original_name;
if(subItem.year !== undefined)
newItem.year = subItem.year;
}
});
joinedItems.push(newItem);
});
return joinedItems;
};
let result = getResult(arrPages, arrArticles);
console.log(result);
In the first part of the code i create a var that has the json data.
To solve the problema i create 2 functions, the order of the creation dosen't metter, the first function getJSONData() take the json data as parameter and return a object filtered by the keys defined in the array keys. The secound function just check if the current key is present in the array of keys, this function could be replaced by the jQuery.inArray() method.
// JSON data
var json = [{
"id":"265",
"title":"Battle of Gettysburg",
"page_id":"4849",
"language_id":"en",
"original_time":"July 1\u20133, 1863"
},
{
"id":"185",
"original_name":"United States",
"country_id":"24",
"article_id":"265"
},
{
"id":"73",
"month":"July",
"year":"1863",
"suffix":"",
"article_id":"265"
}]
// keys that i want
var keys = ["title", "original_name", "year"];
// var that will have the filtered data
var newJSON = getJSONData(json);
console.log(JSON.stringify(newJSON))
// this is the main function of the code
// here we iterate in the json creating a new object that has all the tags definid in the keys array
function getJSONData(arrayJSON){
var JSONFiltered = {};
for(var i in arrayJSON){
for(var key in arrayJSON[i]){
if(hasElement(key)){
JSONFiltered[key] = arrayJSON[i][key];
}
}
}
return JSONFiltered;
}
// this function is used to check a key is present in the array of keys
function hasElement(key){
for(var elem in keys){
if(keys[elem] == key) return true;
}
return false;
}
I am working with an API right now and I am using details[5].Value to target information in the following format:
details:
"value":[
{
"ID": "6",
"Name": "Links",
"Value": "URL"
},
{
"ID": "7",
"Name": "Other",
"Value": "URL"
}
etc
]
The problem is that the location inside of the JSON response is likely to change in the future, making my code obsolete and as the url has the potential to change as well, I cannot target that.
I want a way to target the value of url, mostly, because of this, by the value of the "Name" property. However, if I use something like
_.where(details, { Name: "Links" }).Value
It comes back as undefined. I am not sure if there would be another way to get to the information?
There are a couple points of confusion here.
_.where returns an array:
Looks through each value in the list, returning an array of all the values that contain all of the key-value pairs listed in properties.
so your _.where(details, obj).Value will (almost) always give you undefined because an array is unlikely to have a Value property. _.findWhere on the other hand does return a single value:
Looks through the list and returns the first value that matches all of the key-value pairs listed in properties.
Secondly, your details appears to look like:
let details = {
value: [
{ ID: '6', Name: 'Links', Value: 'URL' },
{ ID: '7', Name: 'Other', Value: 'URL' },
...
]
}
so you don't want to search details, you want to search details.value.
Putting them together:
_(details.value).findWhere({ Name: 'Links' }).Value
or
_.findWhere(details.value, { Name: 'Links' }).Value
You could use Array.prototype.find (or Array.prototype.filter if you're looking for all matches) and write your own callback but you already have Underscore available so why bother? Furthermore, Backbone collections have findWhere and where methods and there are advantages to matching Backbone's overall terminology.
Take a look at this mini function. Let me know if there is something wrong
Update
This is the ES5 Version
function f(key, value, array){
return array.value.filter(function(sub_array){
return sub_array[key] == value;
});
}
This is the ES6 Golfed Version
f=(k,v,a)=>a.value.filter(_=>_[k]==v)
//This is your JSON
var details = {
value: [
{
"ID": "6",
"Name": "Links",
"Value": "URL"
},
{
"ID": "7",
"Name": "Other",
"Value": "URL"
}
]
}
// Short code
f=(k,v,a)=>a.value.filter(_=>_[k]==v)
// f is the function name
// Recives k = array key, v = value, a = array
// filter by the given key and value
// return the result as an array
console.log(f('Name', 'Links', details))
An alternative is using the Javascript built-in function find to get a specific object within an array.
This alternative allows you to pass either an object or a string.
If the byThis parameter is an object, the whole set of key-values must match with the key-values of every object within the target array.
Otherwise, if byThis is a string every object will be treated as string to make the necessary comparison.
let details = { "value": [{ "ID": "6", "Name": "Links", "Value": "URL" }, { "ID": "7", "Name": "Other", "Value": "URL" }]};
let findBy = (array, byThis) => {
return array.find(o => {
if (typeof byThis === 'object') return Object.keys(byThis).every(k => o[k] === byThis[k]);
else if (typeof byThis === 'string') return o.toString() === byThis;
});
}
let found = findBy(details.value, {Name: "Links"});
console.log(found);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I'm attempting my first forray into jQuery. I'm trying to acheive the following, though I'm not sure of the terminology so will try to explain with an example using a kind of C#/pseudocode syntax.
Say I want an (anonymous) object as parameter, looking something like:
elemParameter {
elemId,
arg1,
optionalArg2
}
and I want to pass an array/collection of these objects into my function
$(document).ready(function() {
$.myFunction(
new { Id = "div1", Color = "blue", Animal = "dog" },
new { Id = "div3", Color = "green" },
new { Id = "div4", Color = "orange", Animal = "horse" }
);
}
and then in my function, I need to access each object of the collection, something like:
(function($) {
$.myFunction(var elemParams) {
foreach (param in elemParams) {
$('#' + param.Id).onclick = function() {
this.css('background-color', param.Color);
alert(param.Animal ?? 'no animal specified');
}
}
}
}
Can someone give me a few pointers to the correct syntax for passing parameters this way? Or suggest a better way of acheiving the same if this isn't the correct way to go about things in javascript.
Your syntax is just a bit off, it would look something like this:
$(function() {
function myFunction() {
$.each(arguments, function(i, arg) {
$('#' + arg.Id).click(function() {
$(this).css('background-color', arg.Color);
alert(arg.Animal || 'no animal specified');
});
});
}
myFunction({ Id: "div1", Color: "blue", Animal: "dog" },
{ Id: "div3", Color: "green" },
{ Id: "div4", Color: "orange", Animal: "horse" });
});
You can try a demo here, the syntax style is called JavaScript object literal notation, that's what you're googling for when looking for more info around this :)
Alternatively you can pass the objects in as an array if you want other arguments in addition to these, rather than using arguments directly.
You're looking for "object literal notation." It looks like this:
{
propertyName: propertyValue,
propertyName2: propertyValue2
}
You don't use the new keyword with them, they're just a literal construct like strings ("foo") or numbers (42). Similarly, you have array literals:
["one", "two", "three"]
Here's your example updated:
$(document).ready(function() {
$.myFunction(
// <== Start an array literal with [
[
// <== Colons rather than equal signs
{ Id: "div1", Color: "blue", Animal: "dog" },
{ Id: "div3", Color: "green" },
{ Id: "div4", Color: "orange", Animal: "horse" }
// End the array literal with ]
]
);
}
Note that it's important not to have a trailing comma in either an object or array literal, e.g.
["one", "two", "three", ]
^--- Don't do that
{foo: "bar", x: 27, }
^------- Or that
The question of whether they were valid was unclear (it's clear now as of the recent 5th edition) and IE (at least) chokes on them.
Off-topic, but typically property names in JavaScript code are in camelCase and start with a lower case letter (so, for instance, animal rather than Animal). This is purely style, however.