Javascript data structure for constant string mapping? - javascript

What data type(s) would provide easy lookup for a finite mapping of a pair of strings to one string
S X S |-> S ?
I wish to represent the mapping this way rather than as a function in order to make it easy to extend with simple rules.
Do I need to use arrays, e.g.
[ [[S1, S2], S3 ], [[S1, S3], S4 ], ...]
to represent the tuples? That seems to make lookups horrid. . . but I know that in ES6 keys can't be objects, so I see no other way..

A nested map (simple search tree) provides a generic solution:
const map = {
"one": {
"one": "11",
"two": "12"
},
"two": {
"one": "21",
"two": "22"
}
};
console.log(map["one"]["one"]); // "11"
console.log(map["one"]["two"]); // "12"
console.log(map["two"]["one"]); // "21"
console.log(map["two"]["two"]); // "22"
Alternatively, you can also concatenate two strings into a single key. But you need to prevent key collisions such as "a" + "ab" and "aa" + "b", e.g. by prefixing the key with the length of the first string:
const map = {
"3:oneone": "11",
"3:onetwo": "12",
"3:twoone": "21",
"3:twotwo": "22"
}
function set(map, str1, str2, val) {
return map[str1.length + ":" + str1 + str2] = val;
}
function get(map, str1, str2) {
return map[str1.length + ":" + str1 + str2];
}
console.log(get(map, "one", "one")); // "11"
console.log(get(map, "one", "two")); // "12"
console.log(get(map, "two", "one")); // "21"
console.log(get(map, "two", "two")); // "22"
If you do not like prefixing with the string length, you could e.g. use a separator "a" + ":" + "ab" but would then need to escape the separator character within the two strings. Personally, I prefer the nested map as demonstrated above for its genericity and simplicity.

Related

How to compare individual properties of an array of objects

I have a single array of objects.
The objects are different variations of this:
{
"animals": {
"name": "elephant",
"diet": "herbivore",
"color": "spotted",
"age": "12",
"numbers": ["11", "10", "24", "9"]
}
}
How to iterate through the collection and determine if there are differences between the "numbers" properties or "diet" properties? As in:
In the array, 50 elephant object's "diet" property read "herbivore", but one it reads "omnivore". Break out because it's different and report it.
Or as in:
In the array, 70 elephant object's "numbers" property are the same (even if some are out of order). Report they are the same.
What did I try?:
Stringifying the objects and comparing them, but that won't work because I'm not trying to compare whole objects (just a few individual properties).
I can make this work with a for loop, but it seems stupid overkill and not very efficient for a collection that potentially has hundreds of objects in it.
This is pseudo code, don't freak out:
var isValid = true;
//go through each element in the array
for(let i = 0, i < array.length, i++) {
//check if you have exceeded length of the array
if(i + 1 <= array.length) {
//sort the numbers
var sortedLowerNumbers = sortFunction(array[i].numbers);
var sortedhigherNumbers = sortFunction(array[i+1].numbers);
//compare equality
isValid = json.stringify(sortedLowerNumbers) === json.stringify(sortedhigherNumbers);
if(!isValid) {
break
}
}
}
My research
None of the questions seem applicable. They're seem to be either comparing multiple arrays rather than one or comparing entire JSON objects for equality rather than comparing individual properties.
Is there a smarter way to do this?
Although a for loop is the best and most simple way to do it. if you are looking for brevity you can use .every():
let arr = [{
name: "elephant",
diet: "herbivore",
color: "spotted",
age: "12",
numbers: ["11", "10", "24","9"]
},{ name: "elephant",
diet: "herbivore",
color: "spotted",
age: "12",
numbers: ["11", "10", "24","9"]
},{
name: "elephant",
diet: "herbivore",
color: "spotted",
age: "12",
numbers: ["11", "10", "24","9" , "23"]
}];
let arrSort = arr[0].numbers.sort();
let JSONarrSort = JSON.stringify(arrSort);
console.log(arr.every(x => JSON.stringify(x.numbers.sort()) === JSONarrSort));
console.log(arr.every(x => x.diet === arr[0].diet));

How to create an object containing a range of values for a key

I'm trying to create an object that should hold a range of values for a key which I would use as a look-up table. For example, it needs to "catch" values in the ranges: 500-524, 600-650, etc..
e.g.:
const numbers = {
500 to 524: "20",
600 to 650: "25"
}
And I would like to access the value in the following way:
user.list.map(list => numbers[user.points]).
I know I can put all the values from the range as keys but that would be highly inefficient:
const numbers = {
"500": "20",
"501": "20",
"502": "20",
"503": "20",
(...)
}
So, is it possible to include ranges somehow?
You could set it up like this:
numbers = [
{"start": 500, "end": 524, "value": "20"},
...
...
]
function getFromNumbers(num) {
for(let i=0; i<numbers.length; i++) {
numVal = numbers[i];
if(numVal.start <= num && numVal.end >= num)
return numVal.value;
}
}
console.log(user.list.map(list => getFromNumbers(user.points)))
Why don't you create an object like this:
const numbers =
[
{
start : 500
end : 524
value :20
},
{
start : 600
end : 650
value : 25
}
]
I think you should inverse the solution. You can create object, where key is "20" and value is array of values. For example:
const numbers = {
"20": _.range(500, 524)
}

Regex for substring on NodeJS using pinch.js

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.

Recursively create strings from objects

Hello I have json file:
var jsonData = {
"name": "James",
"age": 22,
"nodes": [
{
"name": "John",
"age": 24,
"nodes": [
{
"name": "Jack",
"age": 65,
"nodes": [
{
"name": "Harry",
"age": 70,
"nodes": []
}
]
},
{
"name": "Joe",
"age": 10,
"nodes": []
}
]
},
{
"name": "Daniel",
"age": 30,
"nodes": []
}
]
}
I need a function that returns output like this:
James 22
James - John 24
James - John - Jack 65
James - John - Jack - Harry 70
James - John - Joe 10
James - Daniel 30
I tried to use recursive function but I don't know how to return output like this one and return age only on last child..
Code:
var json = jsonData;
var prev = [];
function sortData(obj, prev) {
var i = 0;
prev.push(obj.name + " " + obj.age);
console.log(prev);
if (obj.nodes.length > 0) {
while (i < obj.nodes.length) {
sortData(obj.nodes[i], prev);
i++;
prev.pop();
}
}
}
sortData(json, prev);
My function returns output in multiple arrays, so I don't know how to operate with that to return output like that. Will be grateful for any help. Thanks!
You might consider just passing around strings representing the current property path recursively, that way you can just concatenate and pass the value around without worrying about it being mutated. Also, it'll be a lot easier to use array methods like forEach than to use an i variable and manually iterate:
var jsonData={"name":"James","age":22,"nodes":[{"name":"John","age":24,"nodes":[{"name":"Jack","age":65,"nodes":[{"name":"Harry","age":70,"nodes":[]}]},{"name":"Joe","age":10,"nodes":[]}]},{"name":"Daniel","age":30,"nodes":[]}]}
function getAge({ name, age, nodes }, oldPropStr = '') {
const propStr = (oldPropStr ? oldPropStr + ' - ' : '') + name;
console.log(propStr + ' ' + age);
nodes.forEach(node => getAge(node, propStr));
}
getAge(jsonData);
Also, you might note that the current variable name of jsonData is misleading. There's no such thing as a "JSON Object". If you have an object or array, then you have an object or array, full stop. JSON format is a method of representing an object in a string, like const myJSON = '{"foo":"bar"}'. If there are no strings, serialization, or deserialization involved, then JSON is not involved either. Maybe call the variable people or something instead?

Javascript create array of Object property values

Given this Object in Javascript
{
"0": "hello",
"1": { text: "world" }
}
What's the shortest way to create this array from that object?
[
"hello",
{ text: "world" }
]
I've seen somebody using Array.prototype.splice(theObject) but it doesn't seem to work for me.
Update:
Order of output needs to be guaranteed
0 and 1 can be any arbitrary string value and the order at property level needs to be maintained in the corresponding array.
Needs to work with Node.js 6
Just use Object.values:
console.log(Object.values({
"0": "hello",
"1": { text: "world" }
}));
If you want to be sure that the original keys of the object correspond to the positions in the resulting array, you can't rely on Object.values() or a for ... in loop. You can use Object.keys() and sort that array.
var keys = Object.keys(yourObject);
keys.sort();
var array = keys.map(key => yourObject[key]);
Now understand that the call to .sort() can include a comparator function to impose any ordering desired on the original object keys. The sample in the OP is very simple, and the above would work. However more complicated keys might require a custom comparator.
This should maintain the order based on the keys, including where keys have more than one digit
const test = {
"0": "hello",
"3": "three",
"1": { text: "world" },
"2": "two",
"11": "eleven",
}
const transform = obj => Object.keys(obj)
.sort((a, b) => parseInt(a) > parseInt(b) ? 1 : -1)
.map(key => obj[key])
console.dir(transform(test))
let src = {
"0": "hello",
"1": {
text: "world"
}
}
let res = [src].map(it => [it['0'], it['1']])
console.log(res)
Try using for..in loop like this:
let obj = {
"0": "hello",
"1": { text: "world" }
}
let result = []
for(var value in obj){
result.push(obj[value])
}

Categories