Calculate a diff patch of an array in JavaScript - javascript

I want to generate an object that will effectively be able to be applied as a patch to array A in order to produce array B.
Given a function isSame which compares two values and returns true if they're the same, or false otherwise (and the purpose of which is to compare two array elements), is there a known algorithm to calculate the difference between two arrays and return a list of specific differences? The differences would be composed of sets of: X elements removed at index Y and the following elements inserted at index Y.
I've written something that sort of works, but it's buggy at the moment and I'm having trouble moving forward with it, and I'm worrying that I'm reinventing the wheel when somebody else may have already done this. http://jsfiddle.net/G6tYt/1/

If you want the comparison to be both deep and tolerant of objects and arrays both, then this tool I wrote a while back might be of use:
https://github.com/danski/spahql/blob/master/src/SpahQL.DataHelper.js#L18
SpahQL.DataHelper.compare(
{"a": "aval", "b": "bval", "c": "cval", "arr": [0,1,2]},
{"a": "modified", "c": "cval", "d": "added", "arr": [0,1,2,3]}
);
// -> {"/": "~", "/a": "~", "/b": "-", "/d": "+", "/arr": "~", "/arr/3": "+"}

Related

Making a list out of comparison

For the past few days i've been looking into Javascript and how to make list out of comparison, i found something about arrays, and while i knew that this is what i need, I couldn't understand how it exactly worked with my very little knowledge about Javascript.
I want to make a list, say we have 5 items (let's take a, b, c, d, e as examples) and you're asked to compare one to another (do u like "a" or "d"?) and each time you choose an item it gets a point and it keeps asking you until all the comparisons are made with each item, then a list is made out of those comparisons and the items with the most points are ranked first.
if there is an article, a video, a code that i can analyze, anything would help.
Thanks in advance
Here is an idea of how you could approach it.
So you could have the following setup:
const items = ["a", "b", "c", "d", ...and so on]
const pointsRecord = {"a": 0, "b": 0, "c": 0, ...and so on}
With these two pieces you can build pairs to compare, and keep track of the points of each item.
From your description it sounds like you want to compare every item with every other item on the list. You will probably have to loop through the list twice and build a comparison pair that you can output, or render on the screen.
You could write a function to build comparison pairs to look something like this:
const comparisonPairs = [
[ "a", "b" ],
[ "a", "c" ],
[ "a", "d" ],
[ "b", "c" ],
...and so on
]
With something like this you could iterate through comparisonPairs, render the pair on the screen or output to the terminal. Then take the input, and update the pointsRecord object. Something like:
pointsRecord = {
...pointsRecord, // copy over all the data from the original point record
[selectedItem]: pointsRecord[selectedItem] + 1 // increment the points of the selected item
}
After updating pointsRecord you could move on to the next pair.
To sort them at the end you would just have to run a sort function on the items. Something like:
const sortedItems = items.slice().sort((a, b) => pointsRecord[b] - pointsRecord[a]);
Adding the .slice() creates a new array so you don't alter the original typically this is good practice but you may want to sort the original in place depending on your use case.
This is just one approach. For sure you want to think about how you want to organize the data, which it sounds like you are already doing so you are on the right track. As you are working through this if you find that it's getting messy, updating things, and keeping track of the data, that may be a good sign to re-asses and re-think if there is a better way to store the information.

Does JSON.parse() really sort properties when the key names have numeric values?

There's a number of posts here about this issue, and they all contain a lot of assertions that can be summarized like this:
Object properties are never guaranteed to be ordered in any way.
JSON.parse() never sorts properties in any way.
Obviously we tend to have no doubt about #1 above, so we may reasonably expect that, for any operation, properties are processed merely in the order they appear.
[edit, following the #Bergi's comment: or at least they should appear in a random order]
Then from that we might especially infer that #2 should be true.
But look at this snippet:
(BTW note: to show the results, snippets below don't use console.log() which may itself change order of the output. Instead objects are iterated by for (key in obj) and the output displayed in the document)
var inputs = [
'{"c": "C", "a": "A", "b": "B"}',
'{"3": "C", "1": "A", "2": "B"}',
'{"c": "C", "a": "A", "3": "C", "1": "A", "b": "B", "2": "B"}'
];
for (var i in inputs) {
var json = inputs[i],
parsed = JSON.parse(json),
output = [];
for (var j in parsed) {
output.push(j + ': ' + parsed[j]);
}
document.write(`JSON: ${json}<br />Parsed: ${output.join(', ')})<hr />`);
}
It shows that, given a JSON string having unordered keys:
When the input has keys with non-numeric values, the parsed object has its properties in the same order than in the input. This is consistent with the #2 assumption above.
Conversely when the input has keys with numeric values (though they're strings, so not firing parse error), the parsed object has its properties sorted. This now contradicts the #2 assumption.
More: when there are mixed numeric and non-numeric key values, first appear the numeric properties sorted, then the non-numeric properties in their original order.
From that I was first tempted to conclude that actually there would be a (non-documented?) feature, so JSON.parse() works following the "rules" exposed above.
But I had the idea to look further, so the snippet below now shows how ordered are the properties of a merely coded object:
var objects = [
[
'{"c": "C", "a": "A", "b": "B"}',
{"c": "C", "a": "A", "b": "B"}
],
[
'{"3": "C", "1": "A", "2": "B"}',
{"3": "C", "1": "A", "2": "B"}
],
[
'{"c": "C", "a": "A", "3": "C", "1": "A", "b": "B", "2": "B"}',
{"c": "C", "a": "A", "3": "C", "1": "A", "b": "B", "2": "B"}
]
];
for (var i in objects) {
var object = objects[i],
output = [];
for (var j in object[1]) {
output.push(j + ': ' + object[1][j]);
}
document.write(`Code: ${object[0]}<br />Object: ${output.join(', ')}<hr />`);
}
It results in analogue observations, i.e. whichever order they're coded, properties are stored following the 3rd rule above:
numerically named properties are all put first, sorted
other properties are set next, ordered as coded
So it means that JSON.parse() is not involved: in fact it seems to be a fundamental process of object building.
Again this appears not documented, at least as far I could find.
Any clue for a real, authoritative, rule?
[Edit, thanks to #Oriol's answer] It actually appears that, synthetically:
This behaviour conforms to an ECMA specification rule.
This rule should apply to all methods where a specific order is guaranteed but is optional for other cases.
However it seems that modern browsers all choose to apply the rule whatever method is involved, hence the apparent contradiction.
The properties of an object have no order, so JSON.parse can't sort them. However, when you list or enumerate the properties of an object, the order may be well-defined or not.
Not necessarily for for...in loops nor Object.keys
As fully explained in Does ES6 introduce a well-defined order of enumeration for object properties?, the spec says
The mechanics and order of enumerating the properties is not specified
But yes for OrdinaryOwnPropertyKeys
Objects have an internal [[OwnPropertyKeys]] method, which is used for example by Object.getOwnPropertyNames and Object.getOwnPropertySymbols.
In the case of ordinary objects, that method uses the OrdinaryGetOwnProperty abstract operation, which returns properties in a well-defined order:
When the abstract operation OrdinaryOwnPropertyKeys is called with
Object O, the following steps are taken:
Let keys be a new empty List.
For each own property key P of O that is an integer index, in
ascending numeric index order
Add P as the last element of keys.
For each own property key P of O that is a String but is not
an integer index, in ascending chronological order of property creation
Add P as the last element of keys.
For each own property key P of O that is a Symbol, in ascending chronological order of property creation
Add P as the last element of keys.
Return keys.
Therefore, since an order is required by OrdinaryOwnPropertyKeys, implementations may decide to internally store the properties in that order, and use it too when enumerating. That's what you observed, but you can't rely on it.
Also be aware non-ordinary objects (e.g. proxy objects) may have another [[OwnPropertyKeys]] internal method, so even when using Object.getOwnPropertyNames the order could still be different.
so we may reasonably expect that, for any operation, properties are processed merely in the order they appear
That's where the flaw in the reasoning lies. Given that object properties aren't guaranteed to be ordered, we have to assume that any operation processes properties in any order that it sees fit.
And in fact engines evolved in a way that treat integer properties specially - they're like array indices, and are stored in a more efficient format than a lookup table.

How to keep order from json.parse? [duplicate]

I have some data which I originally stored in a generic Javascript object, with the ID as a key:
{
"7": {"id":"7","name":"Hello"},
"3": {"id":"3","name":"World"},
...
}
However, I discovered that browsers do not guarantee a particular object order when looping through them, so in the above "3" would come before "7". I switched to using an array format like this:
[
{"id":"7","name":"Hello"},
{"id":"3","name":"World"},
...
]
Now, I can loop in the correct order but cannot do fast lookups, e.g. data["3"] without having to loop through the array.
Is there a good way to combine both approaches? I would rather avoid using a separate object for each format, because the object is pretty large (hundreds of elements).
I have run across this problem as well. A solution is to keep an ordered array of keys in addition to the original object.
var objects = {
"7": {"id":"7","name":"Hello"},
"3": {"id":"3","name":"World"},
...
}
var order = [ "3", "7", ... ];
Now if you want the second element you can do this lookup:
var second_object = objects[order[1]];
The ECMA standard does not say anything about the order of the elements in an object. And specifically Chrome reorders the keys when they look like numbers.
Example:
var example = {
"a": "a",
"b": "b",
"1": "1",
"2": "2"
};
if you print this in Chrome will get something like:
{
1: "1",
2: "2",
"a": "a",
"b": "b"
};
It's a little sour .. but life.
You could use the solution Andy linked as well, basically wrapping these two together in one object.
An alternative that I use a lot is a custom map function that allows you to specify the order in which the object is traversed. Typically you will do sorting when you're printing your data to the user so while you loop and create your table rows (for instance) your iterator will pass the rows in the order your sort function specifies. I thought it was a nice idea :)
The signature looks like:
function map(object, callback, sort_function);
Example usage:
map(object, function (row) {
table.add_row(row.header, row.value);
}, function (key1, key2) {
return object[key1] - object[key2];
});
Rather than coding your own, there are off-the-shelf libraries available to provide "as provided" JSON parsing or "consistently sorted" JSON printing for display.
You might well consider either of these:
The 'json-order' package offers parsing, formatting & pretty-printing with stable ordering. This is based on having ordered input.
The 'fast-json-stable-stringify' package offers deterministic formatting based on sorting.

How to keep an Javascript object/array ordered while also maintaining key lookups?

I have some data which I originally stored in a generic Javascript object, with the ID as a key:
{
"7": {"id":"7","name":"Hello"},
"3": {"id":"3","name":"World"},
...
}
However, I discovered that browsers do not guarantee a particular object order when looping through them, so in the above "3" would come before "7". I switched to using an array format like this:
[
{"id":"7","name":"Hello"},
{"id":"3","name":"World"},
...
]
Now, I can loop in the correct order but cannot do fast lookups, e.g. data["3"] without having to loop through the array.
Is there a good way to combine both approaches? I would rather avoid using a separate object for each format, because the object is pretty large (hundreds of elements).
I have run across this problem as well. A solution is to keep an ordered array of keys in addition to the original object.
var objects = {
"7": {"id":"7","name":"Hello"},
"3": {"id":"3","name":"World"},
...
}
var order = [ "3", "7", ... ];
Now if you want the second element you can do this lookup:
var second_object = objects[order[1]];
The ECMA standard does not say anything about the order of the elements in an object. And specifically Chrome reorders the keys when they look like numbers.
Example:
var example = {
"a": "a",
"b": "b",
"1": "1",
"2": "2"
};
if you print this in Chrome will get something like:
{
1: "1",
2: "2",
"a": "a",
"b": "b"
};
It's a little sour .. but life.
You could use the solution Andy linked as well, basically wrapping these two together in one object.
An alternative that I use a lot is a custom map function that allows you to specify the order in which the object is traversed. Typically you will do sorting when you're printing your data to the user so while you loop and create your table rows (for instance) your iterator will pass the rows in the order your sort function specifies. I thought it was a nice idea :)
The signature looks like:
function map(object, callback, sort_function);
Example usage:
map(object, function (row) {
table.add_row(row.header, row.value);
}, function (key1, key2) {
return object[key1] - object[key2];
});
Rather than coding your own, there are off-the-shelf libraries available to provide "as provided" JSON parsing or "consistently sorted" JSON printing for display.
You might well consider either of these:
The 'json-order' package offers parsing, formatting & pretty-printing with stable ordering. This is based on having ordered input.
The 'fast-json-stable-stringify' package offers deterministic formatting based on sorting.

reverse json javascript

Is there an inexpensive way to reverse:
{
"10": "..."
"11": "...",
"12": "...",
"13": "...",
"14": "...",
}
so that I get:
{
"14": "...",
"13": "...",
"12": "..."
"11": "...",
"10": "...",
}
reverse() doesn't seem to work on json objects. The only way I can think of is to loop through all the elements and create an array. feels like there should be a better way.
Edit: thanks for all the help UPDATE:
What about let's say if each key has chronological data. When I use $.each on the object, it runs through the objects from top to bottom, I didn't realize that was unreliable.
Here's what I'm trying to do:
$.each(object, function (key, value) {
function foo (key, value);
});
I want to not run foo on all but the last 3 pairs, that is I only want to use the last 3 pairs. I figured if I can reverse them I can just run the first three and stop.
Is there any way I can just do the last 3? If the last 3 ordering is unreliable, is there a safer way to grab the last 3. The last 3 will have the largest numerical keys.
Thanks.
Edit 2:
I'm basically deciding finally to do the manipulations on the server side. I'm reorganizing my database so that the relevant subdocuments are now full on documents that could be queried with mongodb. Thanks.
Javascript associative arrays are unordered. You cannot depend on the properties being in any particular order.
From Mozilla Developer Network:
Although ECMAScript makes iteration
order of objects
implementation-dependent, it may
appear that all major browsers support
an iteration order based on the
earliest added property coming first
(at least for properties not on the
prototype). However, in the case of
Internet Explorer, when one uses
delete on a property, some confusing
behavior results, preventing other
browsers from using simple objects
like object literals as ordered
associative arrays. In Explorer, while
the property value is indeed set to
undefined, if one later adds back a
property with the same name, the
property will be iterated in its old
position--not at the end of the
iteration sequence as one might expect
after having deleted the property and
then added it back.
So if you want to simulate an ordered
associative array in a cross-browser
environment, you are forced to either
use two separate arrays (one for the
keys and the other for the values), or
build an array of single-property
objects, etc.
Use this on json objects arrays
jsonObjectArray.reverse();
$.each(jsonObjectArray, function(i, item) {
//do something with the item
});
Hope this helps
This might help. Get all the keys from the json objects into an array, which you can sort.
var a = { 1 : 'x', 3 : 'y', 2 : 'z' };
var keys = []
for (i in a) { keys.push(i); }
keys.sort();
then you can use reverse() and slice() to just iterate over the keys you need.
$.each(keys, function(idx, key) {
// do whatever with a[key]
});
Follow the json in reverse order,
for(json.length;i>=0;i--)
{
console.log(json.[i]);
}

Categories

Resources