I have an array containing objects with this structure:
var results = [{
AuthorId: 2,
Id: 89,
caseId: 33 //some key
},...];
Now, I want to check if the objects in this array exist 2 or more times and log them in the console.
My approach:
$.each(results, function (i, result) {
var stringRes = result.AuthorId + ";" + result.caseId;
$.each(results, function (j, toTest) {
if (j <= results.length - 2) {
var stringToTest = results[j + 1].AuthorId + ";" + results[j + 1].caseId;
if (stringToTest == stringRes) {
console.log(result.Id);
//some function to do something with duplicates
}
}
});
});
Firstly, I know making strings and comparing them isn't really good. Secondly, this will log every item at least once, because every item compares to each other (= the item compares itself to itself).
Is this fixable with a (more or less) fast and reliable way?
You could use a hash table or a map for counting. If the count is 2 or greater make something. As key, I suggest to use the stringified object, if the object has always the same structure.
var results = [{ AuthorId: 2, Id: 89, caseId: 33 }, { AuthorId: 2, Id: 89, caseId: 33 }],
hash = Object.create(null);
results.forEach(function (a) {
var key = JSON.stringify(a);
hash[key] = (hash[key] || 0) + 1;
if (hash[key] >= 2) {
console.log('count of 2 or more of ' + key);
}
});
Related
I have an array of objects. These objects have a property id. I need a function which returns the next available id (which is not used by an object).
array = [
{
id: 1
},
{
id: 2
},
{
id: 5
},
{
id: 3
}
]
I would like to have a function which takes an array as an input and returns a number (which is the next free id).
In the example case:
findFreeId(array){
magic happens
}
result --> 4
How about something like this?
function findFreeId (array) {
const sortedArray = array
.slice() // Make a copy of the array.
.sort(function (a, b) {return a.id - b.id}); // Sort it.
let previousId = 0;
for (let element of sortedArray) {
if (element.id != (previousId + 1)) {
// Found a gap.
return previousId + 1;
}
previousId = element.id;
}
// Found no gaps.
return previousId + 1;
}
// Tests.
let withGap = [{id: 1}, {id: 2}, {id: 5}, {id: 3}];
let noGap = [{id: 1}, {id: 2}];
let empty = [];
console.log(findFreeId(withGap)); // 4
console.log(findFreeId(noGap)); // 3
console.log(findFreeId(empty)); // 1
A simple approach is to get all the ID values, sort them, then starting at 0 look for the first missing number in the sequence. That may be OK where efficiency doesn't matter, but a more efficient method is to:
Get the IDs
Sort them
Step through the values to get the next available number
Insert the value in the list of IDs
Store the value so next time it starts at #3 from the previous value + 1
E.g.
class IDStore {
constructor(dataArray) {
if (!Array.isArray(dataArray)) {
return null;
}
this.previousIndex = 0;
this.indexes = dataArray.map(obj => obj.id).sort();
}
get nextIndex() {
while (this.indexes[this.previousIndex] == this.previousIndex) {
this.previousIndex++;
}
return this.previousIndex;
}
addIndex(index) {
if (!Number.isInteger(index) || this.indexes.find[index]) {
return null;
}
this.indexes.push(index);
this.indexes.sort();
return index;
}
}
var data = [ { id: 1 }, { id: 2 }, { id: 5 }, { id: 3 } ];
// Create an ID store
var idStore = new IDStore(data);
// Insert more objects in the array with unique IDs
for (var i=0, next; i<4; i++) {
// Current list of indexes
console.log('Indexes: ' + idStore.indexes);
// Get the next available index
next = idStore.nextIndex;
console.log('Next available: ' + next);
// Calling nextIndex doesn't affect next index
next = idStore.nextIndex;
console.log('Next available: ' + next);
// Use next index
data.push({id: next});
// Adding next index is manual
idStore.addIndex(next);
console.log('Added: ' + next);
}
// Data structure is independent
console.log('End: ' + JSON.stringify(data));
This is somewhat simplistic in that it assumes the IDs are sequential integers starting at 0 and doesn't have much validation or error handling.
Maintaining the id is separate from adding new members to the data array. It would be much better to combine the operations, so an "add object" method gets the next available ID, adds it to the object, adds the object to the array, updates the index and returns the new ID.
const findFreeId = (ids) => {
let id = 0;
for (id; ; id++) {
let isFree = true;
for (let i = 0; i < array.length; i++) {
const e = ids[i];
if (e === id) {
isFree = false;
break;
}
}
if (isFree) break;
}
return id;
}
I've got this data:
const items = [
{
_id: 0,
content: 'Item 1 something',
note: 'Some note for item 1'
},
{
_id: 5,
content: 'Item 1.1 something',
note: 'Some note for item 1.1'
},
{
_id: 1,
content: 'Item 2 something',
note: 'Some note for item 2',
subItems: [
{
_id: 2,
parent_id: 1,
content: 'Sub Item 1 something',
subItems: [{
_id: 3,
parent_id: 2,
content: 'Sub Sub Item 4'
}]
}
]
}
];
Using Javascript, how can I navigate/insert into the tree, provided at any point I have the _id of one item in the tree.
For example, some case scenarios:
I am at _id 3 and want to insert another sibling to _id 3
I am at _id 2 and want to go up to _id 1 - how do I get _id 1?
I am at _id 5 and want to go to _id 1
How do I navigate the tree using only an _id?
You could iterate the array and test if _id property has the wanted value. Then save either the node, the parent or the next item of the array.
For getting the parent node, the actual parent is saved as a closure and returned if the wanted _id is found.
All functions test for subItems as array and if, it performs an iteration over subItems.
function getNode(array, id) {
var node;
array.some(function iter(a) {
if (a._id === id) {
node = a;
return true;
}
return Array.isArray(a.subItems) && a.subItems.some(iter);
});
return node;
}
function getParent(array, id) {
var parent ;
array.some(function iter(p) {
return function (a) {
if (a._id === id) {
parent = p;
return true;
}
return Array.isArray(a.subItems) && a.subItems.some(iter(a));
};
}(undefined));
return parent;
}
function getNextNode(array, id) {
var node;
array.some(function iter(a, i, aa) {
if (a._id === id) {
node = aa[i + 1];
return true;
}
return Array.isArray(a.subItems) && a.subItems.some(iter);
});
return node;
}
var items = [{ _id: 0, content: 'Item 1 something', note: 'Some note for item 1' }, { _id: 5, content: 'Item 1.1 something', note: 'Some note for item 1.1' }, { _id: 1, content: 'Item 2 something', note: 'Some note for item 2', subItems: [{ _id: 2, parent_id: 1, content: 'Sub Item 1 something', subItems: [{ _id: 3, parent_id: 2, content: 'Sub Sub Item 4' }] }] }];
console.log(getNode(items, 3));
console.log(getParent(items, 2));
console.log(getNextNode(items, 5));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Using Javascript, how can I navigate/insert into the tree, provided at any point I have the _id of one item in the tree.
You have to recursively loop over the tree and keep track of where you are and where you have been.
JavaScript references point in one direction. Given nothing but a reference to the object with _id 1, you have no connect to the array it is in at all. (It could even exist in multiple arrays or multiple places in the same array).
In general, you need to search the tree (recursion is your friend) and track the indexes of the members you care about.
Once you know the indexes you are dealing with, you can use splice.
I've developed solution for issues like that. I called it backlooker. My function looks:
var backlooker = function(obj) {
for (key in obj) {
if (obj[key]._) {
break;
}
if (obj[key] instanceof Object) {
obj[key]._ = obj;
backlooker(obj[key])
}
}
return obj;
}
You must improve your object first:
items = backlooker(items);
And now you are able to do sth like this:
a = items[2].subItems[0].subItems[0];
c = a._._._._._._._;
c == items; //true
Only one issue: code will not work properly if you already have keys named _ in your object (I think that's very rare situation, but possible).
it's not as easy as "navigating".. the following function will loop through the object and provide things that you can use to achieve what you want.. like name, values, types, number of children and depths.
also look below for specific examples for your case
Main function
you simply call it like this: loopThrough(items)and watch your console for details.
function loopThrough(obj, depth) {
if (typeof(depth) === "undefined") {
depth = 0; // depth 0 means the "root" of your object
} else {
depth++ // increase depth if exist... depth 1 means a property of an object on depth 0
}
for (keyName in obj) {
let thisObj = obj[keyName] //value of this object
let type = thisObj.constructor.name // type: Array, Object, String, Number or Function...
if (type === "Object" || type === "Array") { // to check if this object "have children" to loop through
let childCount = type === "Object" ? Object.keys(thisObj).length : thisObj.length
console.group(depth + " (" + type + ") " + keyName + " : " + childCount) // starts a collapsable group called: depth, type and key
loopThrough(thisObj, depth) //loop through the child object
console.groupEnd() // closes the group
} else { // doesn't have children (a String, Number or Function)
console.log(depth + " (" + type + ") " + keyName + " : " + thisObj) // types: depth, type key and value
}
}
}
Example
here's an example targeting _id:3 in this example I added a sibling to the wanted key.
loopThrough(items, "_id", 3)
function loopThrough(obj, wantedKey = "", wantedValue = "", depth) {
if (typeof(depth) === "undefined") {
depth = 0;
} else {
depth++
}
for (keyName in obj) {
let thisObj = obj[keyName]
let type = thisObj.constructor.name
if (type === "Object" || type === "Array") {
let childCount = type === "Object" ? Object.keys(thisObj).length : thisObj.length
loopThrough(thisObj, wantedKey, wantedValue, depth)
}
if (keyName === wantedKey && thisObj === wantedValue){
siblings = Object.keys(obj)
console.log('%c Hello!, I am ' + wantedKey +":"+ wantedValue, 'color: green');
console.log('%c I have '+ siblings.length + " siblings: " + siblings.toString(), 'color: green');
console.log("%c adding a new sibling...", 'color: grey')
obj["new_sibling"] = "new_sibling_value" // add a sibling to _id 3
siblings = Object.keys(obj)
console.log('%c now I have '+ siblings.length + " siblings: " + siblings.toString(), 'color: green');
console.log('%c I am at depth ' + depth, 'color: blue');
console.log('%c it should be simple to find a way to get my parent _id at depth ' + (depth - 1) , 'color: blue');ParentID
console.log(JSON.stringify(items, null, 4));
}
}
}
for your 2nd request you'll have to tweak the function to store the depth of the wanted key and look for its parent _id at depth - 1 by recalling the function or creating another one
for the third request you can count++ the keys and once you find the wantedKey you store the count and loop through again and look for the count - 1 aka previous sibling or count + 1 aka next sibling
as you can see, it's not a simple task, but it's totally possible with some creativity, best of luck.
I have an array currently only with names because I cannot figure out how to add more information but not make the script sort that data. For every entry in the array I wish to add a number between 1-20 for each, and also a count of how many is named that name. So it would something like 1. Nielsen (100,000). It's only a problem with my second function because I need to sort it by length.
<script>
var arr = []
arr[0] = " Nielsen"
arr[1] = " Jensen"
arr[2] = " Hansen"
arr[3] = " Pedersen"
arr[4] = " Andersen"
arr[5] = " Christensen"
arr[6] = " Larsen"
arr[7] = " Sørensen"
arr[8] = " Rasmussen"
arr[9] = " Jørgensen"
arr[10] = " Petersen"
arr[11] = " Madsen"
arr[12] = " Kristensen"
arr[13] = " Olsen"
arr[14] = " Thomsen"
arr[15] = " Christiansen"
arr[16] = " Poulsen"
arr[17] = " Johansen"
arr[18] = " Møller"
arr[19] = " Mortensen"
document.getElementById("liste").innerHTML = arr; // Skriver den oprindelige rækkefølge
function Sorter1() {
arr.sort(); // Sorter efter aflabetisk rækkefølge
document.getElementById("liste").innerHTML = arr; // Skriver rækkefølgen
}
function Sorter2() {
arr.sort(function (a, b) {
return b.length - a.length || // sorter efter længde
a.localeCompare(b); // Sorter efter aflabetisk rækkefølge
});
document.getElementById("liste").innerHTML = arr; // Skriver rækkefølgen
}
</script>
If I understand you correct you would like to create a multidimensional array and then sort it on the name alphabetically and on character count. If that is correct I would suggest you to create an multidimensional object with the data needed. Then you will be able to sort on the name key and preserve the other information correctly.
Check this out, it may get you in the right direction
var arr = [
{
name: 'Nielsen',
num: 1,
count: 100
},
{
name: 'Jensenlongest',
num: 15,
count: 230
},
{
name: 'Jensenlong',
num: 13,
count: 500
},
{
name: 'Jensen',
num: 2,
count: 300
},
{
name: 'Hansen',
num: 5,
count: 400
}
]
// Just adds the unsorted arr to the list for demo purpose
updateList(arr)
// On "Sort by length" button click
document.getElementById('sort-by-length').addEventListener('click', function (event) {
arr.sort(sortNameByLength);
updateList(arr);
})
// On "Sort alphabetically" button click
document.getElementById('sort-alphabetically').addEventListener('click', function (event) {
arr.sort(sortNameAlphabetically);
updateList(arr);
})
// Sort by name alphabetically
function sortNameAlphabetically(a, b) {
return a.name > b.name
}
// Sort by name length
function sortNameByLength(a, b) {
return a.name.length - b.name.length
}
// Updates the list according to the current sorting of the arr
function updateList(names) {
var listHtml = ''
names.forEach(function (item, index) {
listHtml += item.name + ', ' + item.num + ' (' + item.count + ')<br>'
})
document.getElementById("liste").innerHTML = listHtml
}
https://jsfiddle.net/sbe8yzv0/4/
This will result in a list like this.
Hansen, 5 (400)
Jensen, 2 (300)
Jensenlong, 13 (500)
Jensenlongest, 15 (230)
Nielsen, 1 (100)
You can use an array of complex objects with the data structure you like (just be consistent). Then define your own sort() method that will compare only the name parameter of your objects. Here's a simple example:
var arr = [];
arr[0] = {ID: 1, Name: "Nielsen", Value: "100"};
arr[0] = {ID: 2, Name: "Jensen", Value: "200"};
// Sort based on the second column, 'Name'.
function sortByName(){
arr.sort(
function(x, y){
return x.Name > y.Name; // Compare and sort based on the 'Name' column only.
}
);
console.log(arr[0]); // If results are correct this is 'Jensen'.
console.log(arr[1]); // If results are correct this is 'Nielsen'.
}
Adapt this to your needs (add the proper columns and data, add the proper variables, make it so that it shows in your page's HTML) and it will do what you want.
I am attempting to get the following JavaScript comparator function for an array which contains objects (keyed) but the below code isn't working.
I have tried many examples online (changing them per my needs) but so far no good.
See below for the code. What am I doing wrong?
Thank you for your time.
(As an FYI, the teContainer object below get's filled via an AJAX call)
var teContainer = {};
for (var i = 0; i < result.d.length; i++) {
var key = result.d[i].PersonId;
teContainer[key] = {
displayText: result.d[i].DLSPersonId + ' ' + result.d[i].PersonLastName + ', ' + result.d[i].PersonFirstName.substring(0, 5),
tEFirstName: result.d[i].PersonFirstName,
tELastName: result.d[i].PersonLastName,
tEID: result.d[i].PersonId,
orgID: result.d[i].DLS_OrgId
};
}
function tECompare(a, b) {
if (a.tELastName < b.tELastName)
return -1;
if (a.tELastName > b.tELastName)
return 1;
return 0;
}
teContainer.sort(tECompare);
Edit: Thanks to the answer; here is the corrected code which works:
var teContainer = [];
for (var i = 0; i < result.d.length; i++) {
teContainer.push = ({
displayText: result.d[i].DLSPersonId + ' ' + result.d[i].PersonLastName + ', ' + result.d[i].PersonFirstName.substring(0, 5),
tEFirstName: result.d[i].PersonFirstName,
tELastName: result.d[i].PersonLastName,
tEID: result.d[i].PersonId,
orgID: result.d[i].DLS_OrgId
});
}
function tECompare(a, b) {
if (a.tELastName < b.tELastName)
return -1;
if (a.tELastName > b.tELastName)
return 1;
return 0;
}
teContainer.sort(tECompare);
It's not running because sort isn't a method of an object. teContainer is an object, not an array. You're probably getting the following error:
Calling undefined method "sort"
In general your code is good. The main thing about sorting, is that the 'sort' function only works with Array, not with Object.
Assuming an Ajax result formatted as:
var result = {
d: [
{
PersonId: 1,
DLSPersonId: 1,
PersonLastName: "Doe",
PersonFirstName: "John",
DLS_OrgId: 1
},
{
PersonId: 2,
DLSPersonId: 2,
PersonLastName: "Smith",
PersonFirstName: "Alex",
DLS_OrgId: 2
},
{
PersonId: 3,
DLSPersonId: 3,
PersonLastName: "Vera",
PersonFirstName: "Luis",
DLS_OrgId: 3
}
]
};
Here is a demo
*In the demo, I didn,t chnage your teContainer object in case you need it like it is.
I have two arrays (data and data_not_included).Each elemet of those arrays has attridutes id and name. I fill them this way:
data[i] = {
name :products.models[i].get('name'),
id : products.models[i].get('id')
};
Now I want do display the elements in data which are not in data_not_included array. For example I have
data=[{name: Sugar}{id: 1},{name: Butter}{id: 2},{name: Cola}{id: 3}]
// and
data_nat_included = [{name: Sugar}{id: 1},{name: Butter}{id: 2}].
It should display {name: Cola}{id: 3} only.
Here is what I have already done:
for(var j=0;j<data_not_icluded.length;j++)
{
for(var i=0;i<data.length;i++)
{
if(data[i].id != data_not_icluded[j].id ){
//but this doesnt work for me it displayes a lot of element many times
}
}
}
Both answers are asymptotically bad. This means they run in suboptimal time. In other words, they are naive approaches to solving the problem. This problem is more widely known in the domain of databases, where join operation is a commonplace. It is also known that the complexity of a join is O(log n * n + log m * m) where n is the number of elements in first table and m is the number of elements in the second table. This is fewer operations then would be required by naive solution offered in other examples O(n^2).
However, if more is known about your data, as, for example, I would expect that the values are unique and easily serializable to string, you could even reduce the complexity to O(n + m) by simply creating hashes of the objects you want to compare. Here's how to do it:
Where n is the number of elements in the first array and m is the number of elements in the second array.
var data = [{ name: "Sugar" },
{ id: 1 },
{ name: "Butter" },
{ id: 2 },
{ name: "Cola" },
{ id: 3 }];
var dataNatIncluded = [{ name: "Sugar" },
{ id: 1 },
{ name: "Butter" },
{ id: 2 }];
function join(a, b) {
var hashA = {}, hashB = {}, p, result = [];
function setter(hash) {
return function (element) { hash[JSON.stringify(element)] = element; };
}
a.forEach(setter(hashA));
b.forEach(setter(hashB));
for (p in hashB) delete hashA[p];
for (p in hashA) result.push(hashA[p]);
return result;
}
// [{ name: "Cola" }, { id: 3 }]
A simple way to do that:
var vals = [];
for(var i=0;i<data.length;i++)
{
var found = false;
for(var j=0;j<data_nat.length;j++)
{
if(data[i].id == data_nat[j].id ){
found = true;
break;
}
}
if (!found) vals.push(data[i]);
}
JSFiddle
for(var j=0;j<data_not_icluded.length;j++)
for(var i=0;i<data.length;i++)
if(data[i].id != data_not_icluded[j].id )
Think of what this does: For any not included object, show all objects that have not the same id as the current not included one. This will show many items multiple times, and it will show objects that are in "not included" but at another position.
Instead, loop over data, check each that it is not included in data_not_included, and display it otherwise:
dataloop: for (var i=0; i<data.length; i++) {
for (var j=0; j<data_not_included.length; j++)
if (data[i].id == data_not_icluded[j].id)
continue dataloop;
display(data[i]);
}
Or, using some iteration methods of Arrays:
data.filter(function(d) {
return data_not_included.every(function(n) {
return d.id != n.id;
});
}).each(display);