Regex capturing nonexistent grouping when using `.split` - javascript

I'm trying to split a string using regex. Here's what I'm doing so far:
'a.b.c.e[2].z'.split(/(?:\.)|\[(\d+)\]/g);
I'm getting a result of
["a", undefined, "b", undefined, "c", undefined, "e", "2", "", undefined, "z"]
I expected and want a result of
["a", undefined, "b", undefined, "c", undefined, "e", undefined, 2, undefined, "z"]
Or even:
["a", undefined, "b", undefined, "c", undefined, "e", "2", undefined, "z"]
I just don't want that empty string in the results list, other than manually removing this (which I can't do because this is part of a larger regex), is there a way to modify this regex to not have that empty string?
EDIT
It may be unclear as to what I'm exactly trying to do, here's a snippet of the relavant program:
var rawParts = path.split(/\.|\[(\d+)\]|\[(")((?:[^"]|(?:\\.))*)"\]|\[(')((?:[^']|(?:\\.))*)'\]/g);
var rawPart;
var parts = [];
var i, l;
for (i = 0, l = rawParts.length; i < l; i++) {
rawPart = rawParts[i];
if (rawPart !== undefined) {
if (rawParts[i - 1] === '"') {
rawPart = rawPart.replace(/\\"/g, '"');
} else if (rawParts[i - 1] === "'") {
rawPart = rawPart.replace(/\\'/g, "'");
}
parts.push(rawPart);
}
}
I simplified the problem I had above but the idea is I want to remove the empty string when I'm matching a [0] case. I can't remove all empty string because in a case of a.b[''].c the blank string is valid

If you're trying to get a key from an object using that string as path, I think you're complicating things. You can do this with a reducer, and knowing that bracket notation works for both arrays an objects, you'd have to transform e[2] into e.2, then reduce to resolve the path.
var obj = {a: {b: {c: {e: [{},{},{z:'found'}]}}}}
var get = function(path, obj) {
path = path.replace(/\[(.+?)\]/g, '.$1').split('.')
return path.reduce(function(a, x){return a && a[x]}, obj)
}
get('a.b.c.e[2].z', obj) //=> found
If not found you will get undefined.

Related

Why can I not push a number into my array of strings even though I have converted the number back to a string?

Hello there I am trying to do a code wars kata: https://www.codewars.com/kata/54a91a4883a7de5d7800009c/train/javascript
I know what I have done is very long-winded but I wanted to do it step by step before I refactored it.
I have split the string input from the user
Filtered through the array just to get the number and converted it to a NUMBER
Incremented that number by 1
Changed number back to string
Now I want to add my string number to the original array but it doesn't work and I don't understand why :S
I know this is not the final answer to the kata but I am just trying things and wondered why this did not work...
function isNumeric(num){
return !isNaN(num)
}
function incrementString (string) {
const splitString = string.split("");
let numbers = Number(splitString.filter(el => isNumeric(el)).join("")); //[
'f', 'o', 'o', 'b',
'a', 'r', '0', '0',
'4', '2'
]
let incrementNumber = numbers +=1; // 43
let revertNumberToString = incrementNumber.toString(); // "43"
let test = splitString.push(revertNumberToString); // why can I not push the number 43 onto my original array?
console.log(test); // 11? why?
}
incrementString("foobar0042")
It does seem to be working correctly. If you check splitString again after you push to it then it will have all 11 items. That is where the number 11 is coming from. When you save a push to a variable it doesn't make a new array but rather it saves the length of the new array.
console.log(splitString)
// ["f", "o", "o", "b", "a", "r", "0", "0", "4", "2"]
let test = splitString.push(revertNumberToString);
console.log(splitString)
// ["f", "o", "o", "b", "a", "r", "0", "0", "4", "2", 43]
console.log(test); // 11? why?
Javascript push method adds the element to the array and returns the length, that's why you get 11 instead of the array itself. Reference
You could take a different approach by splitting the value into string and number part and take the length of the number part for later padding the value with leading zeroes.
function incrementString(value) {
const
string = (value.match(/\D+/) || [''])[0],
number = (value.match(/\d+/) || ['0'])[0];
return string + (+number + 1).toString().padStart(number.length, 0);
}
function assertEquals(a, b) {
console.log(a === b, a, b);
}
assertEquals(incrementString("foobar000"), "foobar001");
assertEquals(incrementString("foo"), "foo1");
assertEquals(incrementString("foobar001"), "foobar002");
assertEquals(incrementString("foobar99"), "foobar100");
assertEquals(incrementString("foobar099"), "foobar100");
assertEquals(incrementString(""), "1");

Parsing JSON in node.js app [duplicate]

According to the MDN JavaScript documentation you can define object literal property names using integers:
Additionally, you can use a numeric or string literal for the name of a property.
Like so:
me = {
name: "Robert Rocha",
123: 26,
origin: "Mexico"
}
My question is, how do you reference the property that has an integer as a name? I tried the usual me.123 but got an error. The only workaround that I can think of is using a for-in loop. Any suggestions?
You can reference the object's properties as you would an array and use either me[123] or me["123"]
Dot notation only works with property names that are valid identifiers. An identifier must start with a letter, $, _ or unicode escape sequence. For all other property names, you must use bracket notation.
In an object literal, the property name must be an identifier name, string literal or numeric literal (which will be converted to a string since property names must be strings):
var obj = {1:1, foo:'foo', '+=+':'+=+'};
alert(obj[1] + ' ' + obj.foo + ' ' + obj['+=+']); // 1 foo +=+
You can use me[123] or me["123"]. Both work.
You can use bracket notation me[123].
Just in case anyone else was confused by this: using integer (rather than string) property names may give slightly different - though functionally the same - results (depending on the browser) when you have objects within objects.
Simple objects with no nested objects have consistent behavior across browsers (though as the accepted answer says, we need to use brackets instead of dots to access integer property names):
var str_simple = {
a: "b", c: "d", e: "f", g: "h",
};
str_simple.a === "b"; // true
str_simple.e === "f"; // true
var int_simple = {
1: 2, 3: 4, 5: 6, 7: 8,
};
int_simple[1] === 2; // true - must use brackets instead of dots
int_simple[5] === 6; // true
// this works b/c int property names are coerced to strings anyway
int_simple[1] === int_simple['1']; // true
And this nested object with string keys works exactly as expected:
var str_nested = {
a: {b: "c"},
d: {e: "f", g: "h"},
};
str_nested.a; // returns object as expected, no matter the browser - {b: "c"}
str_nested.a.b === "c"; // true
str_nested.d.g === "h"; // true
But this equivalent nested object with integer keys returns slightly different results depending on the browser, though you can still access the nested objects in the same way (so functionally, it still works the same):
var int_nested = {
1: {2: 3},
4: {5: 6, 7: 8},
};
// latest Chrome (57)
// Safari 10 (latest for my Mac, 10.10 Yosemite)
int_nested[1]; // returns object as expected - {2: 3}
int_nested[1][2] === 3; // true
// latest Firefox (52)
int_nested[1]; // RETURNS ARRAY-LIKE OBJECT - Object [ <2 empty slots>, 3 ]
int_nested.length; // undefined because it's not technically an array
int_nested[1][2] === 3; // true - works b/c object was padded with empty slots
// and again, in all browsers, we can exchange the integer keys
// for equivalent strings since property names are coerced to strings anyway
int_nested[1][2] === int_nested['1'][2];
int_nested['1'][2] === int_nested[1]['2'];
int_nested[1]['2'] === int_nested['1']['2'];
This behavior will still be slightly different but functionally the same if you programmatically construct a nested object. For example, say we wanted to write a function that would take a list of pairs (e.g. [[0, 0], [0, 1], [1, 2], [2, 3]]) and convert it into a nested object so we could check if the pair is in the object with O(1) time (e.g. {0: {0: true, 1: true}, 1: {2: true}, 2: {3, true}}). Note that Sets check reference equality and not value equality, so we couldn't store the pair itself in the Set and achieve the same results:
// [[0, 0], [0, 1], [1, 2], [2, 3]] ->
// {
// 0: {0: true, 1: true},
// 1: {2: true},
// 2: {3: true},
// }
function createNestedObject(pairs) {
var obj = {};
for (var pair of pairs) {
var x = pair[0], y = pair[1];
// must create outer object for each unique x or else
// obj[x][y] would fail b/c obj[x] would be undefined
if (!obj.hasOwnProperty(x)) {
obj[x] = {};
}
obj[x][y] = true;
}
return obj;
}
function exists(nested, pair) {
var x = pair[0], y = pair[1];
// uses !! operator so if pair isn't in nested
// we return false instead of undefined
return !!(nested[x] && nested[x][y]);
}
Pairs with strings will work as expected:
var pairs = [["a", "a"], ["a", "b"], ["c", "d"], ["d", "e"]];
var nested = createNestedObject(pairs);
nested; // as expected - {a: {a: true, b: true}, c: {d: true}, d: {e: true}}
exists(nested, ["a", "a"]); // true
exists(nested, ["a", "b"]); // true
exists(nested, ["ZZZ", "ZZZ"]); // false
But in certain browsers, integer pairs will be different but functionally the same:
var pairs = [[0, 0], [0, 1], [1, 2], [2, 3]];
var nested = createNestedObject(pairs);
nested; // in Safari 10/Chrome 57 - returns nested objects as expected
nested; // in Firefox 52 - Object [ Object[2], Object[3], Object[4] ]
// BUT still gives correct results no matter the browser
exists(nested, [0, 0]); // true
exists(nested, [0, 1]); // true
exists(nested, ['0', '0']); // true
exists(nested, [999, 999]); // false
The situation with numeric property names seems more complicated than it is explained in the answers so far. It is true that you can access such properties via for-in loop. However, it might be important to know that for-in loop gives keys as strings, not as numbers as you might expect:
var obj = {1:2};
for (var key in obj) {
alert(typeof(obj[key])); // you get "number" as expected, however
alert(typeof(key)); // you get "string", not "number"
}
A similar thing happens during serialization with JSON:
JSON.stringify( {1:2} ) === '{"1":2}'
So if you code depends on this little detail you better be aware of it.

jQuery.map() and undefined

In playing with jQuery's utility method, jQuery.map(), I noticed that an undefined return value is omitted from the returned array. What is the reason for this?
var letters = ['a', undefined, 'c', undefined, 'e'];
console.log(letters); //["a", undefined, "c", undefined, "e"]
console.log(letters.length); //5
var lettersMapped = $.map(letters, function(item, index){
var item = item;
return item;
});
console.log(lettersMapped); //["a", "c", "e"]
console.log(lettersMapped.length); //3
Per the documentation for jQuery.map:
The function can return:
the translated value, which will be mapped to the resulting array
null or undefined, to remove the item
an array of values, which will be flattened into the full array
In other words, this is the expected behaviour of jQuery.map.

Javascript some() method and additional parameter

I need to check if at least one element in an array pass a condition.
The condition depends on a variable.
For example, I'm using something like this:
function condition(previousValue, index, array) {
return additonalValue.indexOf(previousValue) + previousValue.indexOf(additonalValue) < -1;
}
I can't figure out how can I pass the "additonalValue" parameter to the array.some(condition) expression.
I'm using jQuery so, an alternative for it is welcome.
Is there a way to pass a parameter to array.some() method?
If you want to pass an additional parameter to the function that is placed inside the some method you can use bind.
var myArray = ['a', 'b', 'c', 'd'];
var otherValue = 'e';
function someFunction(externalParameter, element, index, array) {
console.log(externalParameter, element, index, array);
return (element == externalParameter);
}
myArray.some(someFunction.bind(null, otherValue));
This would give you:
e a 0 ["a", "b", "c", "d"]
e b 1 ["a", "b", "c", "d"]
e c 2 ["a", "b", "c", "d"]
e d 3 ["a", "b", "c", "d"]
false
Using a closure looks like the simplest solution :
var additonalValue = 79;
var r = myArray.some(function(previousValue) {
return additonalValue.indexOf(previousValue) + previousValue.indexOf(additonalValue) < -1;
});
The some() function accepts additional arguments in the form of an array, that is set to this inside the callback, so you could pass a number of values that way :
var arr = ['one', 'two', 'three'];
var val = 'two';
var r = arr.some(function(value) {
return this[0] == value;
}, [val]);
FIDDLE

Remove a value from an array in CoffeeScript

I have an array:
array = [..., "Hello", "World", "Again", ...]
How could I check if "World" is in the array?
Then remove it if it exists?
And have a reference to "World"?
Sometimes maybe I wanna match a word with a regexp and in that case I won't know the exact string so I need to have a reference to the matched String. But in this case I know for sure it's "World" which makes it simpler.
Thanks for the suggestions. I found a cool way to do it:
http://documentcloud.github.com/underscore
filter() is also an option:
arr = [..., "Hello", "World", "Again", ...]
newArr = arr.filter (word) -> word isnt "World"
array.indexOf("World") will get the index of "World" or -1 if it doesn't exist. array.splice(indexOfWorld, 1) will remove "World" from the array.
For this is such a natural need, I often prototype my arrays with an remove(args...) method.
My suggestion is to write this somewhere:
Array.prototype.remove = (args...) ->
output = []
for arg in args
index = #indexOf arg
output.push #splice(index, 1) if index isnt -1
output = output[0] if args.length is 1
output
And use like this anywhere:
array = [..., "Hello", "World", "Again", ...]
ref = array.remove("World")
alert array # [..., "Hello", "Again", ...]
alert ref # "World"
This way you can also remove multiple items at the same time:
array = [..., "Hello", "World", "Again", ...]
ref = array.remove("Hello", "Again")
alert array # [..., "World", ...]
alert ref # ["Hello", "Again"]
Checking if "World" is in array:
"World" in array
Removing if exists
array = (x for x in array when x != 'World')
or
array = array.filter (e) -> e != 'World'
Keeping reference (that's the shortest I've found - !.push is always false since .push > 0)
refs = []
array = array.filter (e) -> e != 'World' || !refs.push e
Try this :
filter = ["a", "b", "c", "d", "e", "f", "g"]
#Remove "b" and "d" from the array in one go
filter.splice(index, 1) for index, value of filter when value in ["b", "d"]
A combination of a few answers:
Array::remove = (obj) ->
#filter (el) -> el isnt obj
_.without() function from the underscorejs library is a good and clean option in case you want to get a new array :
_.without([1, 2, 1, 0, 3, 1, 4], 0, 1)
[2, 3, 4]
CoffeeScript + jQuery:
remove one, not all
arrayRemoveItemByValue = (arr,value) ->
r=$.inArray(value, arr)
unless r==-1
arr.splice(r,1)
# return
arr
console.log arrayRemoveItemByValue(['2','1','3'],'3')

Categories

Resources