I am making a simple WebSockets chat, and have two arrays (this is server side, Node.js):
var clients = {1: "info about 1", 2: "info about 2", 3: "info about 3", 4: "info about four", 5: "info about five", 6: "info about six"};
var partners = [[1,3], [2,6], [5, 4]];
Now what I need to do, is look up a clients partner, in the partner array. So, for example, if my script returned 4, it would need to look up 4, and return 5, and if my script returned 1, it would return 3.
I tried just using partners[mynumber][0] but the problem is that mynumber could be either of the values, rather than just the first one.
Well, you have to look for that particular array first:
function partnerOf(someId) {
var res;
for (var i = 0, l = partners.length; i < l; i++) {
if (partners[i][0] === someId) {
res = partners[i][1];
break;
}
}
return res;
}
... but it looks (and is) quite cumbersome. An alternative would be to reorganize partners structure, turning it into an Object:
var partners = {
1: 3,
2: 6,
5: 4
};
Then look-up becomes trivial: you just evaluate partners[someId], that's all. Both more readable and way faster.
You could do
var possiblePartners=partners[mynumber];
var partnerNum=(possiblePartners[0]==myNumber)?possiblePartners[1]:possiblePartners[0];
However, the best thing to do would be to have an associative array with your partners, like this
var partners={1:3,2:6,5:4};
var partnerNum=partners[mynubmber];
Try this:
Array.prototype.different = function(arr2) {
var ret = [];
arr2.sort();
for(var i = 0; i < this.length; i += 1) {
if(arr2.indexOf( this[i] ) > -1){
ret.push( this[i] );
}
}
return ret;
};
Use like this
console.log(yourArray1.different(yourArray2));
I would change the structure of your data to allow easy partner finding
// defined partners DB
var partners = {};
// adds a chat from c1 to c2
function addChat(partners, c1, c2){
partners[c1] = c2;
partners[c2] = c1;
}
function findChatPartner(partners, c1){
return partners[c1];
}
You could use a for loop combined with an if/else statement
for (var i = 0, n = partners.length; i < n; i++) {
if (partners[i][0] === mynumber)
return partners[i][0];
else if (partners[i][1] === mynumber)
return partners[i][1];
}
It iterates through each set of numbers in the array until it finds mynumber, then returns the other number in that set
Related
I've got a small set of sequences of unique values that I want to combine into a single super-sequence where the relative order of each value is maintained, to the extent possible. For instance (quotes around strings ignored for simplicity):
list1 = [Mary, Bob, Sue, Roger]
list2 = [Bob, Alice, Sue, Dave]
list3 = [Mary, Bob, Larry, Sue, Roger]
superSequence = [Mary, Bob, Alice, Larry, Sue, Roger, Dave]
The goal is to generate an object from which the original lists can be recreated, such as:
obj = {
Mary: [1, 3],
Bob: [1, 3],
Alice: [2],
Larry: [3],
Sue: [1, 2, 3],
Roger: [1, 3],
Dave: [2]
}
Ignoring for the moment that object key order isn't guaranteed in all cases, one could iterate over these keys and use the indices in each associated array to regenerate the original lists. Because the super-sequence is represented as a JS object, the values in it must be unique.
Clearly, not every set of sequences can be combined in this way:
list1 = [Mary, Bob, Sue]
list2 = [Bob, Mary, Sue]
superSequence = [Mary, Bob, Sue] OR
superSequence = [Bob, Mary, Sue]
Picking one or the other should be fine in most cases, with bonus points for an algorithm that can highlight where the order was indeterminate.
In researching this, I seem to have stumbled upon an NP-hard problem that's well-studied in compression, bioinformatics, and other fields. There's something called a majority merge algorithm that seems to be a reasonably decent approximation for this problem, but my ability to translate academic paper pseudo-code into something usable has atrophied over the years. So I was hoping to find an actual implementation in JS, or C, or Python or something that doesn't rely on magic libraries.
So after some further thought, I realized there is a much simpler answer to your question. Since we can assume the same name does not occur multiple times in a given list this will work:
var simpleCombine = function (arr1, arr2) {
"use strict";
arr1 = JSON.parse(JSON.stringify(arr1));
arr2 = JSON.parse(JSON.stringify(arr2));
var i, j, arrOut = [];
while (arr1.length) {
var val = arr1.shift(), found = false;
for (j = 0; j < arr2.length; j += 1) {
if (val === arr2[j]) {
//If we wound an overlap then put everything to the left of it
// in arr2 into the final array
found = true;
var pos = arrOut.length;
arrOut.push(val);
var newVals = arr2.splice(0, j);
while (newVals.length) {
arrOut.splice(pos, 0, newVals.pop());
}
arr2.shift(); //get rid of dup
break;
}
}
if (!found) {
//No overlap found, just add it to the out array
arrOut.push(val);
}
}
//anything left in arr2? Add it to out array
arrOut = arrOut.concat(arr2);
//check for duplicates based on user requirement of each item in the
// sequence only occurs once.
for (i = 0; i < arrOut.length; i += 1) {
for (j = i + 1; j < arrOut.length; j += 1) {
if (arrOut[i] === arrOut[j]) {
//If we find an overlap warn the user, and remove the dup.
console.warn('Even with strict ordering, multiple solutions are possible');
arrOut.splice(i,1);
i -= 1;
break;
}
}
}
return arrOut;
};
var findMultipleSCS = function (arr) {
var first = arr.shift();
while (arr.length) {
first = simpleCombine(first, arr.shift());
}
return first;
};
list1 = ["Mary", "Bob", "Sue", "Roger"];
list2 = ["Bob", "Alice", "Sue", "Dave"];
list3 = ["Mary", "Bob", "Larry", "Sue", "Roger"];
console.log(findMultipleSCS([list1, list2, list3]));
My original answer is below because it is more accurate for lists that may contain the same name multiple times.
//This code works for things where the important thing is that order is
//maintained, not that each entry only occurs once
var findSCS = (function () {
'use strict';
var lcsLen, lcsBack, combine;
lcsLen = function(arr1, arr2) {
//This function moves through the arrays developing the
// length of the longest possible sequence of identical order.
var dists = [[0]], i, j;
for (i = 0; i < arr1.length; i += 1) {
dists[i + 1] = [];
for (j = 0; j < arr2.length; j += 1) {
dists[i + 1][0] = 0; // initialize 0'th column/row with 0
dists[0][j + 1] = 0; // this could be done in a separate loop
dists[i + 1][j + 1] = dists[i + 1][j + 1] || 0; // initialize i,j
if (arr1[i] === arr2[j]) {
//if this condition is met then we have a longer overlap
dists[i + 1][j + 1] = dists[i][j] + 1;
} else {
//if not take the max length so far
dists[i + 1][j + 1] = Math.max(dists[i][j + 1], dists[i + 1][j]);
}
}
}
return dists;
};
lcsBack = function (dists, x, y, i, j) {
//this recursive function takes the longest possible array and build
// the actual list starting from the bottom right of the matrix
// created by lcsLen
if (!i || !j) {
return [];
} else if(x[i - 1] === y[j - 1]) {
return lcsBack(dists, x, y, i - 1, j - 1).concat([x[i - 1]]);
} else {
if (dists[i][j-1] > dists[i-1][j]) {
return lcsBack(dists, x, y, i, j-1);
} else {
return lcsBack(dists,x,y,i-1,j);
}
}
};
combine = function (lcs, arr1, arr2) {
//this take the lcs and fills in the non-overlapping part of
// the original lists, creating the scs
var out = JSON.parse(JSON.stringify(arr1));
var i, testing = 0, outPos = 0, positions = [0];
for (i = 0; i < arr1.length && testing < lcs.length; i += 1) {
if (lcs[testing] === arr1[i]) {
positions[testing + 1] = i;
testing += 1;
}
}
testing = 0; outPos = 0;
for (i = 0; i < arr2.length; i += 1) {
if (lcs[testing] === undefined || lcs[testing] !== arr2[i]) {
out.splice(positions[testing] + outPos, 0, arr2[i]);
outPos += 1;
} else {
testing += 1;
outPos += 1;
}
}
return out;
};
return function (arr1, arr2) {
//get the length matrix to determine the maximum sequence overlap
var lcsLenMat = lcsLen(arr1,arr2);
//Take that distance matrix and build the actual sequence (recursively)
var lcs = lcsBack(lcsLenMat, arr1, arr2, arr1.length, arr2.length);
//Build the SCS
var tempScs = combine(lcs, arr1, arr2);
//This code will allow for duplicates, and in your second example
// It will generate a list with two bobs, which is arguably more
// correct for general purpose use.
return tempScs;
}());
var findMultipleSCS = function (arr) {
var first = arr.shift();
while (arr.length) {
first = findSCS(first, arr.shift());
}
return first;
};
list1 = ["Mary", "Bob", "Sue", "Roger"];
list2 = ["Bob", "Alice", "Sue", "Dave"];
list3 = ["Mary", "Bob", "Larry", "Sue", "Roger"];
console.log(findMultipleSCS([list1, list2, list3]));
Most of these ideas where taken from https://en.wikipedia.org/wiki/Longest_common_subsequence_problem and https://en.wikipedia.org/wiki/Shortest_common_supersequence_problem
The order you put these in will determine which non unique solution you get. For instance list1, list2, list3 give you your first answer, however list2, list3, list1 gives you the also correct:
["Mary", "Bob", "Larry", "Alice", "Sue", "Dave", "Roger"]
If you want to maintain the priority order than list1, list2, list3 does have a unique solution and this will alert you of a duplicate possibility with a console.warn looking for duplicates.
Building on the simpleCombine() function from aduss I came up with a solution that seems to work pretty well. It doesn't currently flag that duplicate items in the result are getting deleted, but that could be implemented with some additional logic in the final filter() call.
function combineLists(...lists)
{
var superSequence = lists.slice(1).reduce((list1, list2) => {
var result = [];
// we need to make a copy of list2 since we mutate it in the loop below
list2 = [].concat(list2);
list1.forEach(item => {
var overlapIndex = list2.indexOf(item);
if (overlapIndex > -1) {
// add 1 to overlapIndex so we also splice out the matching item
result = result.concat(list2.splice(0, overlapIndex + 1));
} else {
result.push(item);
}
});
// anything remaining in list2 is by definition not in list1, so add
// those items to the result
return result.concat(list2);
}, lists[0]);
// look back at the list up to the current item and then filter it out if
// there's a duplicate found. this keeps the first instance of each item.
return superSequence.filter((item, i, list) => list.slice(0, i).indexOf(item) == -1);
}
var list1 = ["Mary", "Bob", "Sue", "Roger"],
list2 = ["Bob", "Alice", "Jimmy", "Chuck", "Sue", "Dave"],
list3 = ["Mary", "Bob", "Larry", "Sue", "Roger"];
console.log(combineLists(list1, list2, list3).join(" "));
Write a function that takes in an object like so {1: 4, 2: 10, 5:3} and then return a list of all the numbers described in the object. Each key-value pair describes a number and how many times it should occur in the array.
Example:
{3 : 10,5 : 2}
[3,3,3,3,3,3,3,3,3,3,5,5]
Also account for empty, null, undefined and non-objects in your code that can be passed in
In those cases just return [], the empty list
Here's what I've been able to produce for. I know I have to do a second loop, but I don't understand how to make the numbers appear in the array the number of times described. Here's my progress:
var numObj = {1:4, 2:10, 3:5};
function numDescribed(numObj) {
var numOfNums = [];
for (var x in numObj) {
numOfNums.push(numObj[x]); //this produces an array of [4, 10, 5]
} for (var i = 0; i < numOfNums.length; i++) {
numOfNums.
}
}
var obj = { 3: 10, 5: 2 };
var res = [];
Object.keys(obj).forEach(function(e) {
for (var i = 0; i < obj[e]; i++) {
res.push(e);
}
});
document.write(res);
I don't know if I have the terminology right, but I have created the following function:
function myCollection(zip, municipality, info) {
this.myZip = zip;
this.myMuni = municipality;
this.myInfo= info;
}
Then I create the following array and add data to it:
var myArray = new Array();
myArray[0] = new myCollection("1000", "municipality A", "info A");
myArray[1] = new myCollection("2000", "municipality B", "info B");
myArray[2] = new myCollection("2000", "municipality C", "info C");
myArray[3] = new myCollection("3000", "municipality D", "info D");
What I would like to achieve:
search inside myArray where a certain zip code in myCollection occurs
display the rest of the data from myCollection for that zipcode
do the same for all other occurences of that same zipcode within the collection
A search for zip "2000" in the examply above should output:
2000, municipality B, info B
2000, municipality C, info C
My train of thoughts
I thought I'd find the occurences (indexes) where the string occurs and then retrieve the required data like this:
console.log(myArray[i].myInfo);
where i is the index within the array where
myArray[i].myZip
matches "2000".
However, I can't get the indexOf to work to search for a string within the object I created, nor can I retrieve all the other occurences that match the same criteria.
Did I choose the right way to do this to start with?
How should I approach this, if you know jQuery or other external libraries should be avoided?
I hope I'm a bit clear - thank you for your thoughts and advice.
You could use a good old while loop :
function findAll(array, fn) {
var result = [], i = -1;
while (++i < array.length) {
if (fn(array[i]) === true) result.push(array[i]);
}
return result;
}
Usage example :
var a = [{ a: 1 }, { a: 2 }, { a: 3 }];
var result = findAll(a, function (item) {
return item.a !== 2;
});
result; // [{ a: 1 }, { a: 3 }]
You can simply iterate over object:
search = function (arr, keyName, value) {
if (arr && keyName && value) {
var i, j = arr.length;
for (i = 0; i < j; i += 1) {
if (arr[i][keyName] === value) {
return {
index : i,
item : arr[i]
}
}
};
return {}
};
And then:
search(myArray, 'myZip', 2000);
In this case indexOf is current i value, so you can simply do var c = myArray[i]; to get collection, that satisfies your filter criteria. If you want to return more result than first collection found, simply add them sequentially to a new array and return it at the end.
I am trying to figure out an efficient way to remove objects that are duplicates from an array and looking for the most efficient answer. I looked around the internet everything seems to be using primitive data... or not scalable for large arrays. This is my current implementation which is can be improved and want to try to avoid labels.
Test.prototype.unique = function (arr, artist, title, cb) {
console.log(arr.length);
var n, y, x, i, r;
r = [];
o: for (i = 0, n = arr.length; i < n; i++) {
for (x = 0, y = r.length; x < y; x++) {
if (r[x].artist == arr[i].artist && r[x].title == arr[i].title) {
continue o;
}
}
r.push(arr[i]);
}
cb(r);
};
and the array looks something like this:
[{title: sky, artist: jon}, {title: rain, artist: Paul}, ....]
Order does not matter, but if sorting makes it more efficient then I am up for the challenge...
and for people who do not know o is a label and it is just saying jump back to the loop instead of pushing to the new array.
Pure javascript please no libs.
ANSWERS SO FAR:
The Performance Test for the answers below:
http://jsperf.com/remove-duplicates-for-loops
I see, the problem there is that the complexity is squared. There is one trick to do it, it's simply by using "Associative arrays".
You can get the array, loop over it, and add the value of the array as a key to the associative array. Since it doesn't allow duplicated keys, you will automatically get rid of the duplicates.
Since you are looking for title and artist when comparing, you can actually try to use something like:
var arrResult = {};
for (i = 0, n = arr.length; i < n; i++) {
var item = arr[i];
arrResult[ item.title + " - " + item.artist ] = item;
}
Then you just loop the arrResult again, and recreate the array.
var i = 0;
var nonDuplicatedArray = [];
for(var item in arrResult) {
nonDuplicatedArray[i++] = arrResult[item];
}
Updated to include Paul's comment. Thanks!
Here is a solution that works for me.
Helper functions:
// sorts an array of objects according to one field
// call like this: sortObjArray(myArray, "name" );
// it will modify the input array
sortObjArray = function(arr, field) {
arr.sort(
function compare(a,b) {
if (a[field] < b[field])
return -1;
if (a[field] > b[field])
return 1;
return 0;
}
);
}
// call like this: uniqueDishes = removeDuplicatesFromObjArray(dishes, "dishName");
// it will NOT modify the input array
// input array MUST be sorted by the same field (asc or desc doesn't matter)
removeDuplicatesFromObjArray = function(arr, field) {
var u = [];
arr.reduce(function (a, b) {
if (a[field] !== b[field]) u.push(b);
return b;
}, []);
return u;
}
and then simply call:
sortObjArray(dishes, "name");
dishes = removeDuplicatesFromObjArray(dishes, "name");
Basic sort-then-unique implementation, fiddle HERE:
function unique(arr) {
var comparer = function compareObject(a, b) {
if (a.title == b.title) {
if (a.artist < b.artist) {
return -1;
} else if (a.artist > b.artist) {
return 1;
} else {
return 0;
}
} else {
if (a.title < b.title) {
return -1;
} else {
return 1;
}
}
}
arr.sort(comparer);
console.log("Sorted: " + JSON.stringify(arr));
for (var i = 0; i < arr.length - 1; ++i) {
if (comparer(arr[i], arr[i+1]) === 0) {
arr.splice(i, 1);
console.log("Splicing: " + JSON.stringify(arr));
}
}
return arr;
}
It may or may not be the most efficient, and should be entirely scalable. I've added some console.logs so you can see it as it works.
EDIT
In the interest of saving on the space the function used, I did that for loop at the end, but it seems likely that didn't properly find only unique results (depsite it passing my simple jsfiddle test). Please try replacing my for loop with the following:
var checker;
var uniqueResults = [];
for (var i = 0; i < arr.length; ++i) {
if (!checker || comparer(checker, arr[i]) != 0) {
checker = arr[i];
uniqueResults.push(checker);
}
}
return uniqueResults;
I use this function. its not doing any sorting, but produces result. Cant say about performance as never measure it.
var unique = function(a){
var seen = [], result = [];
for(var len = a.length, i = len-1; i >= 0; i--){
if(!seen[a[i]]){
seen[a[i]] = true;
result.push(a[i]);
}
}
return result;
}
var ar = [1,2,3,1,1,1,1,1,"", "","","", "a", "b"];
console.log(unique(ar));// this will produce [1,2,3,"", "a", "b"] all unique elements.
Below is Henrique Feijo's answer with ample explanation and an example that you can cut and paste:
Goal: Convert an array of objects that contains duplicate objects (like this one)...
[
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 4334,
"name": "Interesting"
}
]
... Into an array of objects without duplicate objects (like this one):
[
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 4334,
"name": "Interesting"
}
]
Explanation provided in the comments:
var allContent = [{
"id": 10620,
"name": "Things to Print"
}, {
"id": 10620,
"name": "Things to Print"
}, {
"id": 4334,
"name": "Interesting"
}]
//Put Objects Into As Associative Array. Each key consists of a composite value generated by each set of values from the objects in allContent.
var noDupeObj = {} //Create an associative array. It will not accept duplicate keys.
for (i = 0, n = allContent.length; i < n; i++) {
var item = allContent[i]; //Store each object as a variable. This helps with clarity in the next line.
noDupeObj[item.id + "|" + item.name] = item; //This is the critical step.
//Here, you create an object within the associative array that has a key composed of the two values from the original object.
// Use a delimiter to not have foo+bar handled like fo+obar
//Since the associative array will not allow duplicate keys, and the keys are determined by the content, then all duplicate content are removed.
//The value assigned to each key is the original object which is along for the ride and used to reconstruct the list in the next step.
}
//Recontructs the list with only the unique objects left in the doDupeObj associative array
var i = 0;
var nonDuplicatedArray = [];
for (var item in noDupeObj) {
nonDuplicatedArray[i++] = noDupeObj[item]; //Populate the array with the values from the noDupeObj.
}
console.log(nonDuplicatedArray)
For those who love ES6 and short stuff, here it's one solution:
const arr = [
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Paul" },
{ title: "sky", artist: "Jon" }
];
Array.from(arr.reduce((a, o) => a.set(o.title, o), new Map()).values());
const arr = [
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Paul" },
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Jon" },
{ title: "cry", artist: "Jon" }
];
const unique = Array.from(arr.reduce((a, o) => a.set(o.title, o), new Map()).values());
console.log(`New array length: ${unique.length}`)
console.log(unique)
The above example only works for a unique title or id. Basically, it creates a new map for songs with duplicate titles.
Below code compares object with JSON as String format and removes duplicates and works fine with simple arrays.
Array.prototype.unique=function(a){
return function(){
return this.filter(a)
}
}(
function(a,b,c){
var tmp=[];
c.forEach(function(el){
tmp.push(JSON.stringify(el))
});
return tmp.indexOf(JSON.stringify(a),b+1)<0
})
If you are using underscore js, it is easy to remove duplicate object.
http://underscorejs.org/#uniq
function remove_duplicates(objectsArray) {
var arr = [], collection = [];
$.each(objectsArray, function (index, value) {
if ($.inArray(value.id, arr) == -1) {
arr.push(value.id);
collection.push(value);
}
});
return collection;
}
I am trying to sort one array, but have a second array stay in sync with the first.
Example:
var a1 = ["human", "animal", "plant"];
var a2 = ["person", "beast", "nature"];
a1.sort();
After the sort I need to arrays to look like this:
a1 = ["animal", "human", "plant"];
a2 = ["beast", "person", "nature"];
Is there an easy way to do this, Maybe using a custom sort function?
You could zip the arrays before sorting, and unzip them after sorting:
var a = ["human", "animal", "plant"],
b = ["person", "beast", "nature"],
zipped = [];
// zip
for (var i=0; i<a.length; i++)
{
zipped.push({a: a[i], b: b[i]});
}
zipped.sort(function (x, y)
{
return x.a - y.a;
});
// unzip
var z;
for (i=0; i<zipped.length; i++)
{
z = zipped[i];
a[i] = z.a;
b[i] = z.b;
}
...but I think #duffymo's got a better suggestion for you. Use an object/hash/associative array/map.
var a = [{key: 'human', value: 'person'},
{key: 'animal', value: 'beast'},
{key: 'plant', value: 'nature'}];
a.sort(function (x, y)
{
return x.key - y.key;
});
Try something like this (untested):
a1.sort(function(a, b) {
if (a > b) {
// swap a2[0] and a2[1]
var tmp = a2[0];
a2[0] = a2[1];
a2[1] = tmp;
return 1
} else if (a < b) {
return -1
} else {
return 0
}
});
Live DEMO
Something like that, play around with the return values to get it perfect
I'd use an associative array and sort the keys. That's what you've got here. I think an associative array is a better encapsulation of the idea.