I know there's a shortcut like using loadash's uniq but I try to remove duplicated array of object using a native for loop.
var json = [
{name:"james"},
{name:"james_x"},
{name:"jame"},
{name:"james_x"}
]
for(var i = 0;i<json.length-1;i++){
if(json[i].name == json[i+1].name){
json.splice(i,i);
}
}
console.log(json);
https://jsfiddle.net/8wwnvzoc/
I REALLY want to know what's wrong.
You shouldn't modify an array while you're iterating over it. It will lead to unexpected behavior. I'd recommend building an array of indices to remove and then remove them once you're fully through the array.
To fix your algorithm, you need to keep track of all the names you've seen. You can use an ES6 set for this.
var json = [
{name:"james"},
{name:"james_x"},
{name:"jame"},
{name:"james_x"}
]
var indicesToRemove = [];
var seen = new Set();
for(var i = 0;i<json.length;i++){
if (seen.has(json[i].name)) {
indicesToRemove.push(i)
} else {
seen.add(json[i].name);
}
}
for(index of indicesToRemove.reverse()) {
// iterating backwards to prevent indices changing on us...
json.splice(index,index);
}
console.log(json);
Try sorting the array before looping over it like this:
var json = [
{name:"james"},
{name:"james_x"},
{name:"jame"},
{name:"james_x"}
]
json = json.sort(function(a, b){a=a.name;b=b.name;return a<b?-1:a>b?1:0});
for(var i = 0;i<json.length-1;i++){
if(json[i].name == json[i+1].name){
json.splice(i,1);
}
}
console.log(json);
Then, the equal values will be near each other so your algorithm will work as expected...
EDIT:
A possibly better approach, though not using a loop:
var json = [
{name:"james"},
{name:"james_x"},
{name:"jame"},
{name:"james_x"}
]
json = json
.sort(function(a, b){a=a.name;b=b.name;return a<b?-1:a>b?1:0})
.reduce(function(a, b){var n=a.length;if(n===0||a[n-1].name!==b.name)a[n]=b;return a},[]);
console.log(json);
You can do it like so:
var json = [{
name: "james"
}, {
name: "james_x"
}, {
name: "jame"
}, {
name: "james_x"
}]
const unique = []
json.forEach(function(item) {
var existingName = unique.find(function(name) { return name.name === item.name})
!existingName ? unique.push(item) : null
})
console.log(unique)
You're not checking all other items when testing for duplication. Further you're modifying the array while iterating over it - this might crash.
var json = [
{ name:"james" },
{ name:"james" },
{ name:"james_x" },
{ name:"james_x" },
{ name:"james_x" },
{ name:"jame" },
{ name:"james_x" },
{ name:"jame" },
];
var i = 0,
index = { /* track what we've seen already */ },
current;
while (i < json.length) {
current = json[i].name;
// check if the name is in index
if (current in index) {
// it is, remove it from the array
json.splice(i, 1);
} else {
// not in index, leave it untouched and add it to the index
// by defining a key in "index" with arbitrary value
index[current] = true;
// only step forward when indexing, not when removing
i++;
}
}
The increment is limited to the indexing branch since the algorithm would accidentally skip the next item if it's also a duplicate (the following items would shift left-/upwards).
See the progress (2nd column is the following iteration step of the 1st column):
When incrementing in both branches:
1 1
2 2
3 3
2 <= dupe 2
2 2 <= accidentally incremented
2 4 missed the previous item
4
Correct incrementation:
1 1
2 2
3 3
2 <= dupe 2 <= not incremented, all OK
2 2
2 4
4
Related
Given an array of arrays, what would be the efficient way of identifying the duplicate item?
var array = [
[
11.31866455078125,
44.53836644772605
],
[ // <-- Here's the duplicate
11.31866455078125,
44.53836644772605
],
[
11.371536254882812,
44.53836644772605
],
[
11.371536254882812,
44.50140292110874
]
]
I've been working on this with lodash as an accepted dependency, and I get how to just return the "unique" list using _.uniqWith and _.isEqual:
_.uniqWith(array,_.isEqual)
With would give the "unique" version of the list:
[
[ 11.31866455078125, 44.53836644772605 ],
[ 11.371536254882812, 44.53836644772605 ],
[ 11.371536254882812, 44.50140292110874 ]
]
But rather than just reporting the unique elements, I need just the element that is duplicated, and ideally the index of the first occurrence.
Is this actually covered in the lodash library by some combination of methods that I'm missing? Or am I just going to have to live with writing loops to compare elements.
Probably just overtired on this, so fresh eyes on the problem would be welcome.
Trying not to rewrite functions if there are library methods that suit, so I basically am stuck with:
Returning just the duplicate or at least the comparison difference with the "unique list".
Basically identifying the "index of" an array within an array. Though I suppose that can be a filter reduction with _.isEqual once the duplicate item is identified.
Trying also to avoid creating an object Hash/Map and counting the occurrences of keys here as well, or at least not as a separate object, and as something that can be done functionally "in-line".
Lodash gives a lot of useful functions to achieve finding the first duplicate index.
Using the _.findIndex() and _.isEqual() the following code will find the first duplicate index:
var duplicateIndex = _.findIndex(array, function(value, index, collection) {
var equal = _.isEqual.bind(undefined, value);
return _.findIndex(collection.slice(0, index), equal) !== -1;
});
or a bit faster but more verbose:
var duplicateIndex = _.findIndex(array, function(value, index, collection) {
var equal = _.isEqual.bind(undefined, value);
return _.findIndex(collection, function(val, ind) {
return ind < index && equal(val);
}) !== -1;
});
Notice that if no duplicate exists, -1 will be returned.
In a few words the algorithm iterates through array and looks back if the current element does not exist already. If it does, just return the current iteration index.
Please check the working demo.
Here's an approach that uses uniqWith(), and difference():
_.indexOf(array, _.head(_.difference(array, _.uniqWith(array, _.isEqual))));
The basic idea is:
Use uniqWith() to remove the duplicates from array.
Use difference() to compare array against the duplicate-free version. This gets us an array of the duplicates.
Use head() to get the first item of the array. This is the duplicate that we're interested in.
Use indexOf() to find the index of the duplicate, in this case it's 1.
However, if you need the index of the original, and not it's duplicate, we have to make some adjustments:
var duplicate = _.head(_.difference(array, _.uniqWith(array, _.isEqual)));
_.findIndex(array, _.unary(_.partial(_.isEqual, duplicate)));
We're still using uniqWith(), and difference() to find the duplicate. But now, we're using findIndex() to get the index. The reason is that we need to use isEqual() to find the first position of the duplicate, not the second. We construct the predicate using partial() and unary(). The result this time is 0.
You can just use plain javascript to do that, it's not that hard, here is my implementation
for (let i = 0; i < array.length; i++) {
for (let j = i + 1; j < array.length; j++) {
// quick elimination by comparing sub-array lengths
if (array[i].length !== array[j].length) {
continue;
}
// look for dupes
var dupe = true;
for (var k = 0; k < array[i].length; k++) {
if (array[i][k] !== array[j][k]) {
dupe = false;
break;
}
}
// if a dupe then print
if (dupe) {
console.debug("%d is a dupe", j);
}
}
}
The nice part about this implementation is that it will print you multiple times that an array at an index is a dupe for multiple dupes, you can use that fact to count your dupes in each index!
This is actually a very efficient way to do this because the inner for loop (j) always runs from the next position of the outer loop (i). so you half your check count.
And here is a plunk
I don't know how to do this other than to just write the algorithm yourself. Both this answer and the other posted ones aren't very efficient but should be fine:
function findIndex(array, startingIndex, value) {
var predicate = _.partial(_.isEqual, value);
var arraySubset = array.slice(startingIndex+1);
var index = arraySubset.findIndex(predicate);
return index === -1 ? index : index+startingIndex+1;
}
function findDuplicates(array) {
return array.map((value, index) => {
return {
value,
index: findIndex(array, index, value)
};
}).filter(info => info.index !== -1);
}
findDuplicates([1, 2, 3, 4, 1, [ 3 ], [ 4 ], [ 3 ] ]);
// [ { value: 1, index: 4 }, { value: [ 3 ], index: 7 } ] // [ { value: 1, index: 4 }, { value: [ 3 ], index: 7 } ]
This basically creates a map of the array, calling .findIndex() on the remainder of the array, noting down the index of any duplicates, returning information about every item that has a duplicate and what the index of the duplicate is.
One nice thing about this is that it will work for triplicates or any amount of occurrences of a value.
I believe constructing a LUT is one of the most efficient ways when it comes to making comparisons. The following method constructs a LUT by utilizing Array.prototype.reduce() and eventually mutates the original array by removing not only one but all duplicate elements regardless of how many there are.
var arr = [
[
11.31866455078125,
44.53836644772605
],
[
11.31866455078125,
44.53836644772605
],
[
11.371536254882812,
44.53836644772605
],
[
11.371536254882812,
44.50140292110874
]
];
arr.reduce((p,c,i)=> { var prop = c[0]+"" + c[1]+"";
p[prop] === void 0 ? p[prop] = i : p.dups.push(i);
return p;
},{dups:[]}).dups.reverse().forEach( i => arr.splice(i,1))
document.write('<pre>' + JSON.stringify(arr, 0, 2) + '</pre>');
However if you would like to have a new array by keeping the original then obviously it would be much faster procedure.
Consider a JSON object with the following data:
{
"results": [
{
flags: [
is_special: true // or false, or non-existing
],
type: "typeone" // or typetwo, or non-existing
},
... // repeats
]
}
What's an efficient way of creating a filter for ng-repeat="result in json.results" that orders results:
by putting all of the is_special=true first, ordered by #2, & #3.
by putting all typeone before typetwo
by putting all typetwo before all the rest (or without any flags/types).
Important: it mustn't fail when some of these properties are non-existent or equal to ""
Thanks
I don't think this is the best way but there is one way of doing this with Underscore JS. Hopefully someone can optimize my code below:
// Grab the items that have the flag
first = _.filter(results, function(r)
{
return r.flags.is_special;
}
// Grab the remainder
remainder = _.where(results, {flags.is_special: false});
// Now grab your second list
second = _.filter(remainder, function(r)
{
return r.type =='typeone';
});
// Again grab the remainder (and so on)
// Then return your 4 lists
orderBy takes an array of arguments, so you can just go down the list:
ng-repeat="result in json.results | orderBy: [isSpecial, typeFilter]"
$scope.isSpecial = function(item) {
for (var i = 0; i < item.flags.length; i++) {
if (item.flags[i]["is_special"])
return true;
}
}
$scope.typeFilter = function(item) {
item.type == "typeone" ? return 1 : return 0;
}
There are two arrays:
itemKeys: [
{
name: "REFOBJTYPE"
},
{
name: "REFOBJKEY"
}
...
]
itemValues: [
{
value: ""
},
{
value: ""
}
]
and an object
ref: {
REFOBJTYPE: 1,
REFOBJKEY: 2,
}
They are fixed and the structure itself cannot be changed.
values of itemValues should be filled with values from ref object,
to get index we have to look up the itemKeys array.
The point of this question: I don't want to use 2 "for" loops to check for each key if it exists in ref. I WANT use JAVASCRIPT specific features like maybe "indexOf", so:
is the ANY OTHER way, rather than TWO FOR-LOOPs to complete this task?
Please don't question why I need this, why don't I like 2 loops. Obviously under any implementation "behind" it will be 2 loops.
I'm not sure if this is what you meant, but if you loop over the itemKeys array, you can easily look up the value associated with the key in the ref object, and then write that out to the itemValues array in one loop.
for (var i = 0; i < itemKeys.length; i++) {
var key = itemKeys[i].name;
var value = ref[key];
itemValues[i].value = value;
}
I have a PHP function that returns a multiple-level JSON object in the format
var data = {
1 : {
1: { Object },
2: { Object },
...,
...
},
2: {
1: { Object },
2: { Object },
...,
...
}
}
I need to get the length of data to initialize a for loop maximum value. By this, I mean the count of the first level entries.
data[1].length returns 22, and data[2].length returns 23 via the console.log printout. This is expected, but data.length returns undefined.
Is there a better method than
var count = 0;
for (var i=1; i< Number.MAX_VALUE; i++){
if (typeof data[i] != 'undefined')
count = i;
}
How About
Object.keys(data).length
If you can control the JSON format, nested arrays seem a better alternative.
If not, you can run something like
var length = 0;
for (var i in data) {
if (isFinite(i) && i > length)
length = i;
}
You can count the number of entries in a loop, like this:
var length = 0;
for (var key in data) {
if (data.hasOwnProperty(key)) {
length++;
}
}
However, if you have control over the returned data, it would probably be easier to return an array instead of an object because the array will already have the length property you want.
Why not just do this instead of assigning numeric properties? Arrays will have the length property you wish:
var data = [
[
{ Object },
{ Object },
...,
...
],
[
{ Object },
{ Object },
...,
...
]
]
I'm trying to make something similar to an array.
I need to be able to "release" an Index (set its value to undefined) but I don't want to lose the Index.
The "released" Indexes should be re-used whenever a new item is put into the array.
I'd like to be able to do something like:
example = new MyArray();
a = example.leaseIndex(); // returns 0
example[a] = "example value";
b = example.leaseIndex(); // returns 1
example[b] = "another value";
example.releaseIndex(0);
c = example.leaseIndex(); // returns 0
example[c] = "yet another value";
In my example leaseIndex finds an available index or if none are available pushes a new item onto the array and returns the index of that item.
I want to do this so that the array doesn't just grow bigger over time needlessly.
I can't remove the "released" items as each item in the array contains a reference to another item in the same array.
I've had minor successes with functions and arrays outside of the main one to keep track of the available indexes and assign and release them, but ideally I'd like the functionality to be part of the main array.
Would I have to add my functions to the Array (or its prototype) or is there another way? As not all my arrays need this functionality.
Hope this makes sense :/
update
I am trying to store wiring loom layouts, which are essentially a net diagram (points and information as to how the points are connected).
The picture shows an example loom. It has 3 Connectors; Red (0) with 2 Lines, Yellow (1) with 3 Lines and Green (2) with 2 Lines.
One of the lines on the Red Connector is Spliced (allowing multiple Lines to connect to a Single Line, the blue square)
This is how that loom would be stored.
loom = {
points = [
{ self: 0, list: [ 2 ] },
{ self: 1, list: [ 7 ] },
{ self: 2, list: [ 0 ] },
{ self: 3, list: [ 7 ] },
{ self: 4, list: [ 6 ] },
{ self: 5, list: [ 7 ] },
{ self: 6, list: [ 4 ] },
{ self: 7, owner: 1, list: [ 1, 3, 5 ] }
],
connectors = [
[ 0, 1 ],
[ 2, 3, 4 ],
[ 5, 6 ]
]
}
the elements in the connectors array contain the indexes of points in the points array.
the list array inside each points object contains the index of its destination(s) which are points too.
Im trying to make functions to help make managing the indexes easier, just wanted to know if there is a way to extend the array, or make something similar that incorporates the functionality. Using static functions would be OK and is what I've been using. I just wanted to see if i could extend the array, or use something like one so I didnt need to use the static functions.
I would add the methods to the Array's prototype, like this:
Array.prototype.leaseIndex = function () {
for (var i = 0; i < this.length; i++) {
if(typeof this[i] === "undefined") {
return i;
}
}
return this.length;
};
Array.prototype.releaseIndex = function (index) {
delete this[index];
};
So, your code would look like this:
example = [];
a = example.leaseIndex(); // returns 0
example[a] = "example value";
b = example.leaseIndex(); // returns 1
example[b] = "another value";
example.releaseIndex(0);
c = example.leaseIndex(); // returns 0
example[c] = "yet another value";
I hope it helps.
Here is a simple implementation using some static functions (without needing to fuss with methods):
var hop = function(obj, prop){
return Object.prototype.hasOwnProperty.call(obj, prop);
};
var leaseIndex = function(arr, value){
var i;
for(i=0; i<arr.length; i++){
if(!hop(arr, i)){
break;
}
}
arr[i] = value;
return i;
};
var releaseIndex = function(arr, i){
delete arr[i];
};
Of course, I have no idea if this is what you really want, since my algorithm is potentially O(N) and I am not sure you need all this complication.