Get all possible pairs out of a list of names - javascript

Let's say I have an array of names such as :
let members = ["John", "Marie", "Ivy", "Daniel"];
Each person has to deliver a unique item to another. Here is an example of 1 combination set :
[
{"John":"Marie"},
{"Marie":"Ivy"},
{"Ivy":"Daniel"},
{"Daniel":"John"},
]
The reciever can only be named once. I'd like to retrieve an array of all combinations possible. What would be the easiest way to get such an array ?
EDIT : Alot of downvotes. Justified I'm afraid. Here is what I worked on, seems... verbose :
//gets all permutations combination of the same array
const perm = members => members.length ? members.reduce((r, v, i) => [ ...r, ...perm([ ...members.slice(0, i), ...members.slice(i + 1) ]).map(x => [ v, ...x ])], []) : [[]]
let permutations = perm(members);
permutations.map(set => {
let valid = true;
let combination = [];
set.map((name, i) => {
let match = {};
if (name === members[i]) {
valid = false;
return
}
match[members[i]] = name;
combination.push(match);
})
if (valid) possibilities.push(combination);
})

try this
let rslt = []
let members = ["John", "Marie", "Ivy", "Daniel"];
members.forEach((member, i) => {
let nextIndex = i + 1;
// if it's the last member
// then pass it to the first one
if (nextIndex === members.length) {
nextIndex = 0;
}
let obj = new Object();
obj[member] = members[nextIndex];
rslt.push(obj);
})
console.log(rslt);

Related

Javascript array filter with conditional output / apply

I'm wondering, if there is a way to filter an array or stream and apply a function A to all matches and a function B to all non-matches in JavaScript. Here is some example code that explains it a bit more:
// initial data
var names = ['Matthias', 'Maria', 'Bob', 'Anton'];
var namesWithM;
var namesWithoutM;
// gets only names starting with M, but not the others at the same time
namesWithM = names.filter(name => name.startsWith('M'))
// conditional lambda version
namesWithM = [];
namesWithoutM = [];
names.forEach(name => name.startsWith('M') ? namesWithM.push(name) : namesWithoutM.push(name));
// conditional classical version
namesWithM = [];
namesWithoutM = [];
names.forEach(function(name) {
if (name.startsWith('M'))
namesWithM.push(name)
else
namesWithoutM.push(name);
});
The very first version handles just the matches but uses filter and not forEach. Is there any way to use filter and apply a function for matches and non-matches at once? Something like this pseudo code:
names.filter(name.startsWith('M')).apply(namesWithM::push).or(namesWithoutM::push);
filter returns an array. So you can use this array to fill with name which either starts with M or not.
In the below example the filter is filling the array with name starts with M. In filter callback the name not starting with M are filled in another array
// initial data
var names = ['Matthias', 'Maria', 'Bob', 'Anton'];
var namesWithM;
var namesWithoutM = [];
namesWithM = names.filter((name) => {
if (!name.startsWith('M')) {
namesWithoutM.push(name)
}
return name.startsWith('M');
});
console.log(namesWithM, namesWithoutM);
I would use reduce to group data into 2 mentioned cases. I don't see any reason to use filter here
let names = ['Matthias', 'Maria', 'Bob', 'Anton'];
let [namesWithM, namesWithoutM] = names.reduce((acc, name) => {
if (name.startsWith('M')) {
acc[0] = [...(acc[0] || []), name]
return acc;
}
acc[1] = [...(acc[1] || []), name]
return acc;
}, [])
// simpler version
console.log(namesWithM, namesWithoutM);
let [namesWithM1, namesWithoutM1] = names.reduce((acc, name) => {
const index = Number(!name.startsWith('M'));
acc[index] = [...(acc[index] || []), name];
return acc;
}, [])
console.log(namesWithM1, namesWithoutM1);
const names = ['Matthias', 'Maria', 'Bob', 'Anton'];
function A(item){
console.log('filtered');
console.log(item);
}
function B(item){
console.log('not-ffiltered');
console.log(item);
}
const filteredNames = names.filter(name => {
const isValid = name.startsWith('M')
if(isValid)
A(name)
else
B(name)
return isValid;
})

Filtering one value and creating new array in JavaScript

I want to filter one value from my first array and create a second array with the filtered value.
So far I have that but it does not seem very efficient.
const blacklist = bookingsList.filter(booking => booking.id === id);
const newBookingList = bookingsList.filter(booking => booking.id !== id);
Is there a better way to do this?
I think something like this would be good on a large array or if testing the condition is expensive because you would only loop through the array once
const array1 = [];
const array2 = [];
for (var i = 0; i < input.length; i++) {
const value = input[i];
( testCondition(value) ? array1 : array2 ).push(value);
}
You can do it with a single iteration by using forLoop like
const blacklist = [];
const newBookingList = [];
bookingsList.forEach(booking => {
if(booking.id === id) {
blacklist.push(booking)
}
else {
newBookingList.push(booking)
}
}
You can use forEach() and ternary operator:
const bookingsList = [{id:'black'},{id:'new'}];
const blacklist = [], newBookingList = [], id='black';
bookingsList.forEach(booking => booking.id === id? blacklist.push(booking.id) : newBookingList.push(booking.id));
console.log(blacklist);
console.log(newBookingList);
let blacklist = []
let newBookingList = []
const ID = 10;
let bookingsList=[{id:10}, {id:20}]
bookingsList.forEach(booking => booking.id === ID ? blacklist.push(booking) : newBookingList.push(booking))
console.log(newBookingList)
console.log(blacklist)
I think you can use forEach for that:
const newBookingList = [];
const blacklist = [];
bookingsList.forEach(function(booking) {
if(booking.id === id){
blacklist.push(booking)
}
if(booking.id !== id){
newBookingList.push(booking)
}
})
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
You can use splice method to retrive and remove filtered value.
var bookingList = [{id : 1, name : "A"}, {id: 2, name: "B"}];
var sBookingList = bookingList.splice(bookingList.map(function(b){return b.name}).indexOf("A"), 1);
console.log(sBookingList);
console.log(bookingList);

Combine elements of an array into a sub array if they are the same

Trying to find an algorithm that will do the following:
let input = [ 'kooty', 'dlnnoo', 'emor', 'dlnnoo', 'kooty', 'aiprs' ]
function combine(input){
// you should return
[ ['kooty', 'kooty'], ['dlnnoo','dlnnoo'], ['emor'], ['aiprs'] ]
}
I got the answer by using Lodash but i was wondering if there was a way without
function combine(input){
let sortedCity = [];
let finalArr = [];
for(let city in input){
sortedCity.push(input[city].toLowerCase().split('').sort().join(''));
}
let a = lodash.groupBy(sortedCity)
return Object.values(a)
}
combine(input)
let input = [ 'kooty', 'dlnnoo', 'emor', 'dlnnoo', 'kooty', 'aiprs' ]
function combine (input) {
let sorted = input.map(cur => cur.toLowerCase().split('').sort().join(''))
let cache = {} // cache value to index of result array
return sorted.reduce((sum, cur) => {
let index = cache[cur]
if (index !== undefined) {
sum[index].push(cur)
} else {
sum.push([cur])
cache[cur] = sum.length - 1
}
return sum
}, [])
}
combine(input)

What is the most efficent way to filter an object with an array of arrays?

I'm trying to filter an Object by an array of arrays, getting back an array of objects.
Like this:
let obj =
{
"a.1":1,
"a.2":2,
"b.1":3,
"b.2":4,
"c.1":5,
"c.2":6
}
let array =
[
["a.1","b.1"],
["a"],
["b","c.1"]
]
let expectedResult =
[
{
"a.1":1,
"b.1":3,
},
{
"a.1":1,
"a.2":2,
},
{
"b.1":3,
"b.2":4,
"c.1":5
},
]
// this is what I came up with
const filterObjectByArray = (obj, arr) =>
Object.keys(obj)
.filter(ch => {
for (var index = 0; index < arr.length; index++)
if (ch.startsWith(arr[index]))
return true;
})
.reduce((ret, key) =>{
ret[key] = obj[key]
return ret
},{})
let result = array.map(arr => filterObjectByArray(obj, arr))
//kind of deepEqual
console.log(JSON.stringify(expectedResult) == JSON.stringify(result))
Is there a easier or more convenient way to do that? I need to do this operation quite often and my object will be up to couple hundreds entries big, so I see a potential bottleneck here.
I would create a one type mapping of the "base" (the letter) to the "real" keys, and then use it to translate the letter to the real keys when create the object.
const obj = {
"a.1": 1,
"a.2": 2,
"b.1": 3,
"b.2": 4,
"c.1": 5,
"c.2": 6
};
const array = [
["a.1", "b.1"],
["a"],
["b", "c.1"]
];
const getBaseKey = (key) => key.match(/^[a-z]+/)[0]; // get the base of the key - the letter. If it's only one letter, you can use key[0]
/** create a one time map of keys by their base **/
const oobjKeysMap = Object.keys(obj).reduce((map, key) => {
const baseKey = getBaseKey(key);
const curr = map.get(baseKey) || [];
curr.push(key);
return map.set(baseKey, curr);
}, new Map());
const result = array.map((sub) => // iterate the array
[].concat(...sub.map((k) => k in obj ? k : oobjKeysMap.get(getBaseKey(k)))) // create the least of "real" keys
.reduce((ret, key) => { // create the object
ret[key] = obj[key];
return ret;
}, {})
);
console.log(result);

javascript and es6 filter array with unique key

I have a list in variable like:
var name_list = some_list
console.log(name_list)
Array[3]
0: Object
name: "Johny"
1: Object
name: "Monty"
2: Object3:
name: "Johny"
I want to get the list with non repetitive list.
How can I do this ?
Update
I tried with this..
var unique_name = [ ...new Set(name_list.map(name => {
return name.name
}))]
It works fine but I want the object that are filtered unique according to name.
Any idea ??
Another approach I don't see in here would be to use a Map
var name_list = [{name: "Johny"}, {name: "Monty"}, {name: "Johny"}];
// Make a mapping of name to object, then pullout objects.
var name_map = new Map(name_list.map(o => [o.name, o]));
var unique_names = [...name_map.values()];
Note, this will take the last object for each name instead of the first, but you could always do name_list.slice().reverse().map( instead of you need specifically the first object found.
reduce over the array keeping a lookup of previous entries to check against.
const arr=[{name:"Johny"},{name:"Monty"},{name:"Johny"}];
function dedupeByKey(arr, key) {
const tmp = {};
return arr.reduce((p, c) => {
const k = c[key];
if (tmp[k]) return p;
tmp[k] = true;
return p.concat(c);
}, []);
}
console.log(dedupeByKey(arr, 'name'));
Or you can filter using a similar approach:
const arr=[{name:"Johny"},{name:"Monty"},{name:"Johny"}];
function dedupeByKey(arr, key) {
const temp = arr.map(el => el[key]);
return arr.filter((el, i) =>
temp.indexOf(el[key]) === i
);
}
console.log(dedupeByKey(arr, 'name'));
Filter to keep only those elements which are the first occurrence of the name (in other words, whose index is the same as the index of the first occurrence):
var name_list = [{name: "Johny"}, {name: "Monty"}, {name: "Johny"}];
var filtered = name_list . filter(
(elt, i, a) => i === a.findIndex(
elt2 => elt.name === elt2.name
)
);
document.getElementById('result').textContent = JSON.stringify(filtered);
<pre id='result'></pre>
This might not be the fastest approach, but it could be the simplest.
You can use this little distinctByProp( theArray, propertyName) function.
I hope it helps
distinctByProp = (theArray, prop) => {
let tempArray = [];
let isFound = obj => tempArray.find( n => n[prop] === obj[prop]);
theArray.forEach( current => !isFound(current) && tempArray.push(current));
return tempArray;
}
Usage is like:
let names_list = [{name: "Johny"}, {name: "Johnyh"}, {name: "Max"}, {name: "Monty"}, {name: "Johnyh"}, {name: "Max"}];
let distinct = distinctByProp(names_list, "name");
console.log(distinct);
I hope it helps
You could use Array#filter().
var name_list = [{ name: "Johny" }, { name: "Monty" }, { name: "Johny" }],
filtered = name_list.filter(a => {
this.my = this.my || Object.create(null);
if (!this.my[a.name]) {
this.my[a.name] = true;
return true;
}
});
document.write('<pre>' + JSON.stringify(filtered, 0, 4) + '</pre>');
This is my resumed form of this type of unique. Not only for one field but for all the root fields of the object.
const unique = l => l.filter(
(e1, i, a) => i === a.findIndex(
e2 => Object.keys(e1)
.map(x => e1[x] === e2[x])
.reduce((x,y) => x && y)
)
)

Categories