Testing anOrdered Collection - collections

|email addressList|
email:= '<abc#gmail.com>,abcd ,<abcef#domail.com>'.
addressList:= MailAddressParser addressesIn:email.
Above code will get me an Ordered collection which will include all the strings, though 'abcd' is not a valid email id its still there in collection.
My question is how can i remove Invalid email address which don't include '#'.
addressList do[:ele | " How can i check each item if it includes # , if not remove it."]

You can take several approaches:
Do exactly what you've asked for (deleting from the existing collection)
addressList removeAllSuchThat: [:string| (string includes: $#) not ]
Or you can generate a new list that includes only the valid elements like Uko proposed. Here you exchange addressList with the new list.
addressList select: [:string| string includes: $# ]
Or you can generate a new list that excludes the invalid elements
addressList reject: [:string| (string includes: $#) not ]

You can use
addressList select: [ :email | email includes: $# ]
if you're looking just for one character like # (as was suggested by #dh82), or if you'll need to look for a substring you can use:
addressList select: [ :email | email includesSubString: '#' ]
select: creates a new collection that with the elements that match the condition specified in a block.

Related

How can I delete all keys that don't match certain names with JQ?

I have a huge JSON file with lots of stuff I don't care about, and I want to filter it down to only the few keys I care about, preserving the structure. I won't bother if the same key name might occur in different paths and I get both of them. I gleaned something very close from the answers to this question, it taught me how to delete all properties with certain values, like all null values:
del(..|nulls)
or, more powerfully
del(..|select(. == null))
I searched high and low if I could write a predicate over the name of a property when I am looking at a property. I come from XSLT where I could write something like this:
del(..|select(name|test("^(foo|bar)$")))
where name/1 would be the function that returns the property name or array index number where the current value comes from. But it seems that jq lacks the metadata on its values, so you can only write predicates about their value, and perhaps the type of their value (that's still just a feature of the value), but you cannot inspect the name, or path leading up to it?
I tried to use paths and leaf_paths and stuff like that, but I have no clue what that would do and tested it out to see how this path stuff works, but it seems to find child paths inside an object, not the path leading up to the present value.
So how could this be done, delete everything but a set of key values? I might have found a way here:
walk(
if type == "object" then
with_entries(
select( ( .key |test("^(foo|bar|...)$") )
and ( .value != "" )
and ( .value != null ) )
)
else
.
end
)
OK, this seems to work. But I still wonder it would be so much easier if we had a way of querying the current property name, array index, or path leading up to the present item being inspected with the simple recusion ..| form.
In analogy to your approach using .. and del, you could use paths and delpaths to operate on a stream of path arrays, and delete a given path if not all of its elements meet your conditions.
delpaths([paths | select(all(IN("foo", "bar") or type == "number") | not)])
For the condition I used IN("foo", "bar") but (type == "string" and test("^(foo|bar)$")) would work as well. To also retain array elements (which have numeric indices), I added or type == "number".
Unlike in XML, there's no concept of attributes in jq. You'll need to delete from objects.
To delete an element of an object, you need to use del( obj[ key ] ) (or use with_entries). You can get a stream of the keys of an object using keys[]/keys_unsorted[] and filter out the ones you don't want to delete.
Finally, you need to invert the result of test because you want to delete those that don't match.
After fixing these problems, we get the following:
INDEX( "foo", "bar" ) as $keep |
del(
.. | objects |
.[
keys_unsorted[] |
select( $keep[ . ] | not )
]
)
Demo on jqplay
Note that I substituted the regex match with a dictionary lookup. You could use test( "^(?:foo|bar)\\z" ) in lieu of $keep[ . ], but a dictionary lookup should be faster than a regex match. And it should be less error-prone too, considering you misused $ and (...) in lieu of \z and (?:...).
The above visits deleted branches for nothing. We can avoid that by using walk instead of ...
INDEX( "foo", "bar" ) as $keep |
walk(
if type == "object" then
del(
.[
keys_unsorted[] |
select( $keep[ . ] | not )
]
)
else
.
end
)
Demo on jqplay
Since I mentioned one could use with_entries instead of del, I'll demonstrate.
INDEX( "foo", "bar" ) as $keep |
walk(
if type == "object" then
with_entries( select( $keep[ .key ] ) )
else
.
end
)
Demo on jqplay
Here's a solution that uses a specialized variant of walk for efficiency (*). It retains objects all keys of which are removed; only trivial changes are needed if a blacklist or some other criterion (e.g., regexp-based) is given instead. WHITELIST should be a JSON array of the key names to be retained.
jq --argjson whitelist WHITELIST '
def retainKeys($array):
INDEX($array[]; .) as $keys
| def r:
if type == "object"
then with_entries( select($keys[.key]) )
| map_values( r )
elif type == "array" then map( r )
else .
end;
r;
retainKeys($whitelist)
' input.json
(*) Note for example:
the use of INDEX
the recursive function, r, has arity 0
for objects, the top-level deletion occurs first.
Here's a space-efficient, walk-free approach, tailored for the case of a WHITELIST. It uses the so-called "streaming" parser, so the invocation would look like this:
jq -n --stream --argjson whitelist WHITELIST -f program.jq input.json
where WHITELIST is a JSON array of the names of the keys to be deleted, and
where program.jq is a file containing the program:
# Input: an array
# Output: the longest head of the array that includes only numbers or items in the dictionary
def acceptable($dict):
last(label $out
| foreach .[] as $x ([];
if ($x|type == "number") or $dict[$x] then . + [$x]
else ., break $out
end));
INDEX( $whitelist[]; .) as $dict
| fromstream(inputs
| if length==2
then (.[0] | acceptable($dict)) as $p
| if ($p|length) == (.[0]|length) - 1 then .[0] = $p | .[1] = {}
elif ($p|length) < (.[0]|length) then empty
else .
end
else .
end )
Note: The reason this is relatively complicated is that it assumes that you want to retain objects all of whose keys have been removed, as illustrated in the following example. If that is not the case, then the required jq program is much simpler.
Example:
WHITELIST: '["items", "config", "spec", "setting2", "name"]'
input.json:
{
"items": [
{
"name": "issue1",
"spec": {
"config": {
"setting1": "abc",
"setting2": {
"name": "xyz"
}
},
"files": {
"name": "cde",
"path": "/home"
},
"program": {
"name": "apache"
}
}
},
{
"name": {
"etc": 0
}
}
]
}
Output:
{
"items": [
{
"name": "issue1",
"spec": {
"config": {
"setting2": {
"name": "xyz"
}
}
}
},
{
"name": {}
}
]
}
I am going to put my own tentative answer here.
The thing is, the solution I had already in my question, meaning I can select keys during forward navigation, but I cannot find out the path leading up to the present value.
I looked around in the source code of jq to see how come we cannot inquire the path leading up to the present value, so we could ask for the key string or array index of the present value. And indeed it looks like jq does not track the path while it walks through the input structure.
I think this is actually a huge opportunity forfeited that could be so easily kept track during the tree walk.
This is why I continue thinking that XML with XSLT and XPath is a much more robust data representation and tool chain than JSON. In fact, I find JSON harder to read even than XML. The benefit of the JSON being so close to javascript is really only relevant if - as I do in some cases - I read the JSON as a javascript source code assigning it to a variable, and then instrument it by changing the prototype of the anonymous JSON object so that I have methods to go with them. But changing the prototype is said to cause slowness. Though I don't think it does when setting it for otherwise anonymous JSON objects.
There is JsonPath that tries (by way of the name) to be something like what XPath is for XML. But it is a poor substitute and also has no way to navigate up the parent (or then sibling) axes.
So, in summary, while selecting by key in white or black lists is possible in principle, it is quite hard, because a pretty easy to have feature of a JSON navigation language is not specified and not implemented. Other useful features that could be easily achieved in jq is backward navigation to parent or ancestor of the present value. Currently, if you want to navigate back, you need to capture the ancestor you want to get back to as a variable. It is possible, but jq could be massively improved by keeping track of ancestors and paths.

jq: Can I use the name of an argument in the code itself?

Is there some way to use the name of an argument in the code?
For example, this command:
jq -n --arg name value '{($name): $name}'
has this output:
{
"value": "value"
}
Is it possible to get this output?:
{
"name": "value"
}
If you remove the ($name): JQ will use the name as the key fallback:
$ jq -n --arg name value '{$name}'
{
"name": "value"
}
$
From the jq Frequently Asked Questions:
Notable Differences between Versions
𝑸: In which version was the abbreviation {$x} for {"x": $x} introduced?
Version 1.5
From the Object Construction ({}) documentation:
The value can be any expression (although you may need to wrap it in parentheses if, for example, it contains colons), which gets applied to the {} expression's input (remember, all filters have an input and an output).
{foo: .bar}
will produce the JSON object {"foo": 42} if given the JSON object {"bar":42, "baz":43} as its input. You can use this to select particular fields of an object: if the input is an object with "user", "title", "id", and "content" fields and you just want "user" and "title", you can write
{user: .user, title: .title}
Because that is so common, there's a shortcut syntax for it:
{user, title}
Not directly. You already know the name you want to use, so either hard code it:
jq -n --arg name value '{name: $name}'
or use a second variable
jq -n --arg key name --arg name value '{($key): $name}'
(I forgot about #0stone0's approach; that's probably what you want to use.)

JQ: remove nested key and keep other main array key intact

I have a json file looking like this:
{
"parents": [{
// array of objects
}],
"modules": {
"a": 1,
"b": 2
}
}
I want to remove they key b of the object modules.
I am running this command: jq "with_entries(.value |= del(.b))"
But this fails when the parents array is present. I get
Cannot index array with string "b"
How can I make the command ignore the parents array and only work on the modules object?
Your idea was right, but you missed the selecting the object desired inside with_entries(), hence your delete operation was attempted on all the objects in your JSON.
Since the parents record is an array type and not an object , the del function throws out an error that its not able to index the array with the given name. You need to do
with_entries( select(.key == "modules").value |= del(.b) )
The select() function filters that object keyed by name "modules" and applies the delete action on that object alone.
jq-play snippet

Rename property of an object inside array

This is my input JSON
{
"uuid":123,
"description": [
{
"car_name":"Toyota",
"saleDate":"23 May 2017"
}
]
}
Expected output:
{
"uuid":123,
"description": [
{
"name":"Toyota",
"saleDate":"23 May 2017"
}
]
}
Rule : .description[]|={name:.car_name,saleDate}
I am able to achieve the desired result with this rule. However, is there a way I can rename the 'car_name' property to 'name' and also not mention all the other properties ( in this case, 'saleDate') in the rule. I might end up having 50+ properties inside the object and I do not want to mention all of them in the rule.
There can be more than one object in the array.
To preserve the order of keys while being sure only to change the specific key name:
jq '.description[] |= with_entries(if .key == "car_name" then .key = "name" else . end)'
I just learned about += from an answer to another question. It works here too.
jq '.description[] |= (select(has("car_name")) += {"name":.car_name} | del(.car_name))'
The select function selects the objects that meet the condition. Then we add to, and delete from, those objects.
Does this do what you want?
jq '.description[]
|= . +
if has("car_name") then {"name":.car_name}
else null end
| del(.description[].car_name)'
The if-then block adds the object {name:.car_name} to any object in the description array that has a key with the name "car_name" and adds nothing (null) to any object in the array that does not have a key with that name.
The del function deletes key-value pairs at the path expression .description[].car_name
This seems to work while preserving the order.
jq '.description[] |= with_entries( .key |= gsub("car_name"; "name") )'
The jq manual explains what with_entries does.
Credit goes to an answer to a similar question by peak.
jq '.description[] |= with_entries(if .key == "car_name" then .key = "name" else . end)'

What is it called when you have multiple data structures, but not connected with json in jq?

For instance, I might have something coming out of my jq command like this:
"some string"
"some thing"
"some ping"
...
Note that there is no outer object or array and no commas between items.
Or you might have something like:
["some string"
"some thing"
"some ping"]
["some wing"
"some bling"
"some fing"]
But again, no commas or outer object or array and no commas between them to indicate that this is JSON.
I keep thinking the answer is that it is called "raw", but I'm uncertain about this.
I'm specifically looking for a term to look for in the documentation that allows you to process the sorts of examples above, and I am at a loss as how to proceed.
To start with, the jq manual.yml describes the behavior of filters this way:
Some filters produce multiple results, for instance there's one that
produces all the elements of its input array. Piping that filter
into a second runs the second filter for each element of the
array. Generally, things that would be done with loops and iteration
in other languages are just done by gluing filters together in jq.
It's important to remember that every filter has an input and an
output. Even literals like "hello" or 42 are filters - they take an
input but always produce the same literal as output. Operations that
combine two filters, like addition, generally feed the same input to
both and combine the results. So, you can implement an averaging
filter as add / length - feeding the input array both to the add
filter and the length filter and then performing the division.
It's also important to keep in mind that the default behavior of jq is to run the filter you specify once for each JSON object. In the following example, jq runs the identity filter four times passing one value to it each time:
$ (echo 2;echo {}; echo []; echo 3) | jq .
2
{}
[]
3
What is happening here is similar to
$ jq -n '2, {}, [], 3 | .'
2
{}
[]
3
Since this isn't always what you want, the -s option can be used to tell jq to gather the separate values into an array and feed that to the filter:
$ (echo 2;echo {}; echo []; echo 3)| jq -s .
[
2,
{},
[],
3
]
which is similar to
$ jq -n '[2, {}, [], 3] | .'
[
2,
{},
[],
3
]
The jq manual.yml explains how the --raw-input/-R option can be included for even more control over input handing:
Don't parse the input as JSON. Instead, each line of text is passed to the filter as a string. If combined with --slurp,then the entire input is passed to the filter as a single long string.
You can see using the -s and -R options together in this example produces a different result:
$ (echo 2;echo {}; echo []; echo 3)| jq -s -R .
"2\n{}\n[]\n3\n"

Resources