Breaking available timeslot array into chunks based on booked slots - javascript

I have 2 arrays:
generated timestamps (30 min each (1800 difference in each timestamp)) from particular start hour to end hour for a day.
I get a booked slots response from the API response.
I need to merge both arrays to form the array of objects representing the available and booked slots:
let availableTimeslots = [
1559709000,
1559710800,
1559712600,
1559714400,
1559716200,
1559718000,
1559719800,
1559721600,
1559723400,
1559725200,
1559727000,
1559728800,
1559730600,
1559732400,
1559734200,
1559736000,
1559737800,
1559739600
];
let bookedTimeSlots = {
bookings: [
{
timestamp: {
from: 1559719800,
to: 1559723400
}
},
{
timestamp: {
from: 1559730600,
to: 1559732400
}
}
]
};
I need to create array of objects, something like:
[
{
available: true,
timeslots: [1559709000, 1559710800, 1559712600, 1559714400, 1559716200, 1559718000]
},
{
available: false,
timeslots: [1559719800, 1559721600, 1559723400]
},
{
available: true,
timeslots: [1559725200, 1559727000, 1559728800]
},
{
available: false,
timeslots: [1559730600, 1559732400]
},
{
available: true,
timeslots: [1559732400, 1559734200, 1559736000, 1559737800, 1559739600]
}
];
I am really confused on how to proceed with this
I am thinking on to replace the value in availableTimeslots with the desired booked slot object and later on all the non-object values with {available: true, timeslots: [...]}
bookedTimeSlots.bookings.map((bs, i)=> {
let ai = availableTimeslots.findIndex(bs.timestamp.from);
ai > -1 && (availableTimeslots[ai]={available: false, x : [..._.range(bs.timestamp.from, bs.timestamp.to, 1800)]});
})
Any help would be appreciated.

There's a lot of ways you could solve this. One way to simplify it is to take advantage of the fact that there's a known increment (1,800) between timeslots, so rather than trying slice up the array of timeslots, you can generate a new array of timeslots for each "chunk" given a start and end timeslot. In the below snippet you can see a basic recursive solution that takes this approach:
const INCREMENT = 1800;
// Utility function to generate an inclusive range
function rangeIncl(start, end, incr = INCREMENT) {
return start < end ? [start, ...rangeIncl(start + incr, end)] : [end];
}
function timeslotGroups(startTimeslot, endTimeslot, bookings) {
const [booking, ...restBookings] = bookings;
if (booking) {
if (startTimeslot < booking.from) {
// startTimeslot is before next booking.from; add available group
return [
{
available: true,
timeslots: rangeIncl(startTimeslot, booking.from - INCREMENT),
},
...timeslotGroups(booking.from, endTimeslot, bookings),
];
}
if (startTimeslot <= booking.to) {
// startTimeslot is between booking.from and .to; add not-available group
return [
{
available: false,
timeslots: rangeIncl(booking.from, booking.to),
},
...timeslotGroups(booking.to + INCREMENT, endTimeslot, restBookings),
];
}
// startTimeslot is after booking.to; try again with next booking
return timeslotGroups(startTimeslot, endTimeslot, restBookings);
}
// No more bookings; add final available group if there are any
// timeslots left
return startTimeslot < endTimeslot ? [
{
available: true,
timeslots: rangeIncl(startTimeslot, endTimeslot),
},
] : [];
}
const availableTimeslots = [
1559709000, 1559710800, 1559712600, 1559714400, 1559716200, 1559718000,
1559719800, 1559721600, 1559723400, 1559725200, 1559727000, 1559728800,
1559730600, 1559732400, 1559734200, 1559736000, 1559737800, 1559739600,
];
const bookedTimeslots = {
bookings: [
{ timestamp: { from: 1559719800, to: 1559723400 }},
{ timestamp: { from: 1559730600, to: 1559732400 }},
],
};
const firstTimeslot = availableTimeslots[0];
const lastTimeslot = availableTimeslots[availableTimeslots.length - 1];
// Bookings will be easier to work with as an array of { from, to } objects
const bookings = bookedTimeslots.bookings.map(booking => booking.
timestamp);
const groups = timeslotGroups(firstTimeslot, lastTimeslot, bookings);
console.log(groups);
Note that this code assumes that bookings will be in chronological order.

I did it with something like this: https://jsfiddle.net/saurabhsharma/a6qoyfhd/
let availableTimeslots = [
1559709000,
1559710800,
1559712600,
1559714400,
1559716200,
1559718000,
1559719800,
1559721600,
1559723400,
1559725200,
1559727000,
1559728800,
1559730600,
1559732400,
1559734200,
1559736000,
1559737800,
1559739600
];
let parsedArr = [];
let bookedTimeSlots = {
bookings: [{
timestamp: {
from: 1559719800,
to: 1559723400
}
},
{
timestamp: {
from: 1559730600,
to: 1559732400
}
}
]
};
/* parsedArr = availableTimeslots.map((ts, i) => {
return bookedTimeSlots.bookings.map((bs, n)=> {
let x = [];
if(ts<bs.timestamp.from) {
x.push(ts);
} else { return {available: true, timeslots: [...x]}}
})
}) */
bookedTimeSlots.bookings.map((bs, i) => {
let ai = availableTimeslots.indexOf(bs.timestamp.from);
if (ai > -1) {
let range = [..._.range(bs.timestamp.from, bs.timestamp.to, 1800)]
availableTimeslots[ai] = {
available: false,
timestamp: [...range]
};
availableTimeslots.splice(ai + 1, range.length - 1);
}
})
console.log("availableTimeSlot", availableTimeslots);
let tempArr = [];
let startIndex = '';
let timeStampParsed = [...availableTimeslots];
while(!timeStampParsed.every((ts)=>typeof ts == 'object')) {
timeStampParsed.map((ts, i) => {
if (typeof ts != "object") {
tempArr.push(ts);
startIndex === '' && (startIndex = i);
//Case i reached the last index
if (i == timeStampParsed.length - 1) {
let range = [..._.range(timeStampParsed[startIndex], timeStampParsed[i]+1800, 1800)];
console.log(range);
timeStampParsed[startIndex] = {
available: true,
timestamp: [...range]
};
timeStampParsed.splice(startIndex + 1, range.length);
tempArr = [];
startIndex = '';
}
} else {
if (tempArr.length > 0 && startIndex !== '') {
let range = [..._.range(timeStampParsed[startIndex], timeStampParsed[i-1], 1800)];
timeStampParsed[startIndex] = {
available: true,
timestamp: [...range]
};
timeStampParsed.splice(startIndex+1, range.length);
tempArr = [];
startIndex = '';
}
}
})
}
console.log("TIMESTAMP PARSED =>", timeStampParsed);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Please suggest a better way to do it.

Related

Issue arranging values in ascending order [duplicate]

This question already has answers here:
Sort Array Elements (string with numbers), natural sort
(8 answers)
Closed 11 months ago.
I am trying to arrange given values in ascending orders
const value = [
{ val: "11-1" },
{ val: "12-1b" },
{ val: "12-1a" },
{ val: "12-700" },
{ val: "12-7" },
{ val: "12-8" },
];
I am using code below to sort this in ascending order:
value.sort((a,b)=>(a.val >b.val)? 1:((b.val>a.val)?-1:0));
The result of this sort is in the order 11-1,12-1a, 12-1b, 12-7, 12-700, 12-8. However, I want the order to be 11-1,12-1a, 12-1b, 12-7, 12-8, 12-700.
How can I achieve that?
If you're only interested of sorting by the value after the hyphen you can achieve it with this code:
const value = [
{val:'12-1'},
{val:'12-700'},
{val:'12-7'},
{val:'12-8'},
];
const sorted = value.sort((a,b) => {
const anum = parseInt(a.val.split('-')[1]);
const bnum = parseInt(b.val.split('-')[1]);
return anum - bnum;
});
console.log(sorted);
updated the answer as your question update here's the solution for this:
const value = [{ val: '11-1' }, { val: '12-1b' }, { val: '12-1a' }, { val: '12-700' }, { val: '12-7' }, { val: '12-8' }];
const sortAlphaNum = (a, b) => a.val.localeCompare(b.val, 'en', { numeric: true });
console.log(value.sort(sortAlphaNum));
You can check the length first and then do the sorting as follow:
const value = [
{ val: "12-1" },
{ val: "12-700" },
{ val: "12-7" },
{ val: "12-8" },
];
const result = value.sort(
(a, b)=> {
if (a.val.length > b.val.length) {
return 1;
}
if (a.val.length < b.val.length) {
return -1;
}
return (a.val >b.val) ? 1 : ((b.val > a.val) ? -1 : 0)
}
);
console.log(result);
little change's to #Christian answer it will sort before and after - value
const value = [{ val: '12-1' }, { val: '12-700' }, { val: '11-7' }, { val: '12-8' }];
const sorted = value.sort((a, b) => {
const anum = parseInt(a.val.replace('-', '.'));
const bnum = parseInt(b.val.replace('-', '.'));
return anum - bnum;
});
console.log(sorted);
If you want to check for different values both before and after the hyphen and include checking for letters, the solution at the end will solve this.
Here's what I did:
Created a regex to split the characters by type:
var regexValueSplit = /(\d+)([a-z]+)?-(\d+)([a-z]+)?/gi;
Created a comparison function to take numbers and letters into account:
function compareTypes(alpha, bravo) {
if (!isNaN(alpha) && !isNaN(bravo)) {
return parseInt(alpha) - parseInt(bravo);
}
return alpha > bravo;
}
Split the values based on regexValueSplit:
value.sort((a, b) => {
let valuesA = a.val.split(regexValueSplit);
let valuesB = b.val.split(regexValueSplit);
This produces results as follows (example string "12-1a"):
[
"",
"12",
null,
"1",
"a",
""
]
Then, since all the split arrays should have the same length, compare each value in a for loop:
for (let i = 0; i < valuesA.length; i++) {
if (valuesA[i] !== valuesB[i]) {
return compareTypes(valuesA[i], valuesB[i]);
}
}
// Return 0 if all values are equal
return 0;
const value = [{
val: "11-1"
},
{
val: "12-1b"
},
{
val: "12-1a"
},
{
val: "12-700"
},
{
val: "12-7"
},
{
val: "12-8"
},
];
var regexValueSplit = /(\d+)([a-z]+)?-(\d+)([a-z]+)?/gi;
function compareTypes(alpha, bravo) {
if (!isNaN(alpha) && !isNaN(bravo)) {
return parseInt(alpha) - parseInt(bravo);
}
return alpha > bravo;
}
value.sort((a, b) => {
let valuesA = a.val.split(regexValueSplit);
let valuesB = b.val.split(regexValueSplit);
for (let i = 0; i < valuesA.length; i++) {
if (valuesA[i] !== valuesB[i]) {
return compareTypes(valuesA[i], valuesB[i]);
}
}
return 0;
});
console.log(JSON.stringify(value, null, 2));
Since you are sorting on string values, try using String.localeCompare for the sorting.
Try sorting on both numeric components of the string.
const arr = [
{val:'12-1'},
{val:'11-900'},
{val:'12-700'},
{val:'12-7'},
{val:'11-1'},
{val:'12-8'},
{val:'11-90'},
];
const sorter = (a, b) => {
const [a1, a2, b1, b2] = (a.val.split(`-`)
.concat(b.val.split(`-`))).map(Number);
return a1 - b1 || a2 - b2; };
console.log(`Unsorted values:\n${
JSON.stringify(arr.map(v => v.val))}`);
console.log(`Sorted values:\n${
JSON.stringify(arr.sort(sorter).map(v => v.val))}`);

How to invert the structure of nested array of objects in Javascript?

I currently have an array that has the following structure:
data = [
{
time: 100,
info: [{
name: "thing1",
count: 3
}, {
name: "thing2",
count: 2
}, {
}]
},
{
time: 1000,
info: [{
name: "thing1",
count: 7
}, {
name: "thing2",
count: 0
}, {
}]
}
];
But I would like to restructure the array to get something like this:
data = [
{
name: "thing1",
info: [{
time: 100,
count: 3
}, {
time: 1000,
count: 7
}, {
}]
},
{
name: "thing2",
info: [{
time: 100,
count: 2
}, {
time: 1000,
count: 0
}, {
}]
}
];
So basically the key would have to be switched from time to name, but the question is how. From other posts I have gathered that using the map function might work, but since other posts had examples to and from different structures I am still not sure how to use this.
There are a number of ways to achieve this however, the key idea will be to perform a nested looping of both data items and their (nested) info items. Doing that allows your algorithm to "visit" and "map" each piece of input data, to a corresponding value in the resulting array.
One way to express that would be to use nested calls to Array#reduce() to first obtaining a mapping of:
name -> {time,count}
That resulting mapping would then be passed to a call to Object.values() to transform the values of that mapping to the required array.
The inner workings of this mapping process are summarized in the documentation below:
const data=[{time:100,info:[{name:"thing1",count:3},{name:"thing2",count:2},{}]},{time:1e3,info:[{name:"thing1",count:7},{name:"thing2",count:0},{}]}];
const result =
/* Obtain array of values from outerMap reduce result */
Object.values(
/* Iterate array of data items by reduce to obtain mapping of
info.name to { time, count} value type */
data.reduce((outerMap, item) =>
/* Iterate inner info array of current item to compound
mapping of info.name to { time, count} value types */
item.info.reduce((innerMap, infoItem) => {
if(!infoItem.name) {
return innerMap
}
/* Fetch or insert new { name, info } value for result
array */
const nameInfo = innerMap[ infoItem.name ] || {
name : infoItem.name, info : []
};
/* Add { time, count } value to info array of current
{ name, info } item */
nameInfo.info.push({ count : infoItem.count, time : item.time })
/* Compound updated nameInfo into outer mapping */
return { ...innerMap, [ infoItem.name] : nameInfo }
}, outerMap),
{})
)
console.log(result)
Hope that helps!
The approach I would take would be to use an intermediate mapping object and then create the new array from that.
const data = [{time: 100, info: [{name: "thing1", count: 3}, {name: "thing2", count: 2}, {}]}, {time: 1e3, info: [{name: "thing1", count: 7}, {name: "thing2", count: 0}, {}]} ];
const infoByName = {};
// first loop through and add entries based on the name
// in the info list of each data entry. If any info entry
// is empty ignore it
data.forEach(entry => {
if (entry.info) {
entry.info.forEach(info => {
if (info.name !== undefined) {
if (!infoByName[info.name]) {
infoByName[info.name] = [];
}
infoByName[info.name].push({
time: entry.time,
count: info.count
});
}
});
}
});
// Now build the resulting list, where name is entry
// identifier
const keys = Object.keys(infoByName);
const newData = keys.map(key => {
return {
name: key,
info: infoByName[key]
};
})
// newData is the resulting list
console.log(newData);
Well, the other guy posted a much more elegant solution, but I ground this one out, so I figured may as well post it. :)
var data = [
{
time: 100,
info: [{
name: "thing1",
count: 3
}, {
name: "thing2",
count: 2
}, {
}]
},
{
time: 1000,
info: [{
name: "thing1",
count: 7
}, {
name: "thing2",
count: 0
}, {
}]
}
];
var newArr = [];
const objInArray = (o, a) => {
for (var i=0; i < a.length; i += 1) {
if (a[i].name === o)
return true;
}
return false;
}
const getIndex = (o, a) => {
for (var i=0; i < a.length; i += 1) {
if (a[i].name === o) {
return i;
}
}
return false;
}
const getInfoObj = (t, c) => {
let tmpObj = {};
tmpObj.count = c;
tmpObj.time = t;
return tmpObj;
}
for (var i=0; i < data.length; i += 1) {
let t = data[i].time;
for (var p in data[i].info) {
if ("name" in data[i].info[p]) {
if (objInArray(data[i].info[p].name, newArr)) {
let idx = getIndex(data[i].info[p].name, newArr);
let newInfoObj = getInfoObj(t, data[i].info[p].count);
newArr[idx].info.push(newInfoObj);
} else {
let newObj = {};
newObj.name = data[i].info[p].name;
let newInfo = [];
let newInfoObj = getInfoObj(t, data[i].info[p].count);
newInfo.push(newInfoObj);
newObj.info = newInfo;
newArr.push(newObj);
}}
}
}
console.log(newArr);
try to use Object.keys() to get the key

Javascript manipulating json issue

[
{
"timing": [
{
"zone": 18.8
},
{
"zone": 17.06,
},
{
"zone": 16.6
},
]
},
{
"timing": [
{
"zone": 12.6,
},
{
"zone": 14.6,
}
]
},
{
"timing": [
{
"zone":19.06,
},{
"zone": 8.06,
}
]
}
]
Here i am trying to work manipulate with one json data using javascript.
But, I am not able to think any approach how to achive that.
I am expecting below json. It give zone1, zone2, zone3 as per the zone any it will be dynamically
Please have a look to below json.
[
{
"zone1": [18.8, 12.6, 19.06 ]
},{
"zone2": [17.06, 14.6, 8.06]
}, {
"zone3":[16.6]
}
]
This is the output of json how it should look like.
Please have a look
You can use reduce and forEach
Loop through data, set OP's initial value as an object
Loop through timing property of each element, check if the zone + index + 1 exists in op or not, if exists push zone to that key else initialise new key
let data = [{"timing": [{"zone": 18.8},{"zone": 17.06,},{"zone": 16.6},]},{"timing": [{"zone": 12.6,},{"zone": 14.6,}]},{"timing": [{"zone": 19.06,}, {"zone": 8.06,}]}]
let final = data.reduce((op, { timing }) => {
timing.forEach(({ zone }, i) => {
let key = `zone${ 1 + i }`
op[key] = op[key] || []
op[key].push(zone)
})
return op
}, {})
console.log(final)
// If you need final output to be array of object just use entries and map to build a desired output
console.log(Object.entries(final).map(([k,v])=>({[k]:v})))
Here's a possible solution
var data = [{
"timing": [{
"zone": 18.8
},
{
"zone": 17.06,
},
{
"zone": 16.6
},
]
},
{
"timing": [{
"zone": 12.6,
},
{
"zone": 14.6,
}
]
},
{
"timing": [{
"zone": 19.06,
}, {
"zone": 8.06,
}]
}
];
// Calculate the total number of zones
var totalZones = 0;
for (let i = 0; i < data.length; i++) {
const currZones = data[i].timing.length;
if (currZones > totalZones) totalZones = currZones;
}
console.log(totalZones);
// Create the final Array
var result = new Array(totalZones);
for (let i = 0; i < totalZones; i++) {
result[i] = {
zone: []
}
}
// Populate the final array with values
for (let i = 0; i < totalZones; i++) {
for (let j = 0; j < data.length; j++) {
let currTiming = data[j].timing[i];
if (currTiming !== undefined) {
let currZone = data[j].timing[i].zone;
if (currZone !== undefined) {
result[i].zone.push(currZone);
}
}
}
}
console.log(result);
1) Gather all zone values into one array of array
2) Calculate max rows needed for zones
3) Have a simple for-loop till max rows and use shift and push methods.
const data = [
{
timing: [
{
zone: 18.8
},
{
zone: 17.06
},
{
zone: 16.6
}
]
},
{
timing: [
{
zone: 12.6
},
{
zone: 14.6
}
]
},
{
timing: [
{
zone: 19.06
},
{
zone: 8.06
}
]
}
];
const zones = data.map(time => time.timing.map(z => z.zone));
const rows = zones.reduce((rows, arr) => Math.max(rows, arr.length), 0);
const all = [];
for (let index = 1; index <= rows; index++) {
const res = [];
zones.forEach(zone => zone.length > 0 && res.push(zone.shift()));
all.push({ [`zone${index}`]: res });
}
console.log(all);
const input = [ {"timing": [{"zone": 18.8},{"zone": 17.06,},{"zone": 16.6},]},{"timing": [{"zone": 12.6,},{"zone": 14.6,}]},{"timing": [{"zone":19.06,},{"zone": 8.06,}]}]
var data = input.map(t => t.timing.map(u => u.zone));
var output = data[0].map((col, i) => data.map(row => row[i])).map((item, index) => {res = {}; res["zone"+(index+1)] = item.filter(t => t!==undefined); return res});
console.log(output);
Not the shortest, but it's very readable.
var json = [{"timing": [{"zone": 18.8},{"zone": 17.06,},{"zone": 16.6},]},{"timing": [{"zone": 12.6,},{"zone": 14.6,}]},{"timing": [{"zone": 19.06,}, {"zone": 8.06,}]}];
// index 0 is zone1, index 1 is zone2, index 2 is zone3, and so on ...
var zones = [];
// Loop through each 'timing' object
json.forEach(function(timingObject) {
var timing = timingObject['timing'];
// loop through each 'zone' in the given 'timing' object
timing.forEach(function(zoneObject, index) {
var zone = zoneObject['zone'];
// if the zone exists in the zones[] defined above
// add the current zone to its place
//
// if not (else), we have to add the array for the
// current index, then add the value of the current zone.
if(zones[index]) {
zones[index]['zone' + (index + 1)].push(zone);
} else {
zones.push({ ['zone' + (index + 1)]: [zone]})
}
});
});
console.log(zones);

Find Duplicate Array By Caption without using multiple loops [duplicate]

I need some help with iterating through array, I keep getting stuck or reinventing the wheel.
values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName1' },
{ name: 'someName1' }
]
How could I check if there are two (or more) same name value in array? I do not need a counter, just setting some variable if array values are not unique. Have in mind that array length is dynamic, also array values.
Use array.prototype.map and array.prototype.some:
var values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName2' }
];
var valueArr = values.map(function(item){ return item.name });
var isDuplicate = valueArr.some(function(item, idx){
return valueArr.indexOf(item) != idx
});
console.log(isDuplicate);
ECMA Script 6 Version
If you are in an environment which supports ECMA Script 6's Set, then you can use Array.prototype.some and a Set object, like this
let seen = new Set();
var hasDuplicates = values.some(function(currentObject) {
return seen.size === seen.add(currentObject.name).size;
});
Here, we insert each and every object's name into the Set and we check if the size before and after adding are the same. This works because Set.size returns a number based on unique data (set only adds entries if the data is unique). If/when you have duplicate names, the size won't increase (because the data won't be unique) which means that we would have already seen the current name and it will return true.
ECMA Script 5 Version
If you don't have Set support, then you can use a normal JavaScript object itself, like this
var seen = {};
var hasDuplicates = values.some(function(currentObject) {
if (seen.hasOwnProperty(currentObject.name)) {
// Current name is already seen
return true;
}
// Current name is being seen for the first time
return (seen[currentObject.name] = false);
});
The same can be written succinctly, like this
var seen = {};
var hasDuplicates = values.some(function (currentObject) {
return seen.hasOwnProperty(currentObject.name)
|| (seen[currentObject.name] = false);
});
Note: In both the cases, we use Array.prototype.some because it will short-circuit. The moment it gets a truthy value from the function, it will return true immediately, it will not process rest of the elements.
In TS and ES6 you can create a new Set with the property to be unique and compare it's size to the original array.
const values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName3' },
{ name: 'someName1' }
]
const uniqueValues = new Set(values.map(v => v.name));
if (uniqueValues.size < values.length) {
console.log('duplicates found')
}
To know if simple array has duplicates we can compare first and last indexes of the same value:
The function:
var hasDupsSimple = function(array) {
return array.some(function(value) { // .some will break as soon as duplicate found (no need to itterate over all array)
return array.indexOf(value) !== array.lastIndexOf(value); // comparing first and last indexes of the same value
})
}
Tests:
hasDupsSimple([1,2,3,4,2,7])
// => true
hasDupsSimple([1,2,3,4,8,7])
// => false
hasDupsSimple([1,"hello",3,"bye","hello",7])
// => true
For an array of objects we need to convert the objects values to a simple array first:
Converting array of objects to the simple array with map:
var hasDupsObjects = function(array) {
return array.map(function(value) {
return value.suit + value.rank
}).some(function(value, index, array) {
return array.indexOf(value) !== array.lastIndexOf(value);
})
}
Tests:
var cardHand = [
{ "suit":"spades", "rank":"ten" },
{ "suit":"diamonds", "rank":"ace" },
{ "suit":"hearts", "rank":"ten" },
{ "suit":"clubs", "rank":"two" },
{ "suit":"spades", "rank":"three" },
]
hasDupsObjects(cardHand);
// => false
var cardHand2 = [
{ "suit":"spades", "rank":"ten" },
{ "suit":"diamonds", "rank":"ace" },
{ "suit":"hearts", "rank":"ten" },
{ "suit":"clubs", "rank":"two" },
{ "suit":"spades", "rank":"ten" },
]
hasDupsObjects(cardHand2);
// => true
if you are looking for a boolean, the quickest way would be
var values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName1' },
{ name: 'someName1' }
]
// solution
var hasDuplicate = false;
values.map(v => v.name).sort().sort((a, b) => {
if (a === b) hasDuplicate = true
})
console.log('hasDuplicate', hasDuplicate)
const values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName4' }
];
const foundDuplicateName = values.find((nnn, index) =>{
return values.find((x, ind)=> x.name === nnn.name && index !== ind )
})
console.log(foundDuplicateName)
Found the first one duplicate name
const values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName4' }
];
const foundDuplicateName = values.find((nnn, index) =>{
return values.find((x, ind)=> x.name === nnn.name && index !== ind )
})
You just need one line of code.
var values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName2' }
];
let hasDuplicates = values.map(v => v.name).length > new Set(values.map(v => v.name)).size ? true : false;
Try an simple loop:
var repeat = [], tmp, i = 0;
while(i < values.length){
repeat.indexOf(tmp = values[i++].name) > -1 ? values.pop(i--) : repeat.push(tmp)
}
Demo
With Underscore.js A few ways with Underscore can be done. Here is one of them. Checking if the array is already unique.
function isNameUnique(values){
return _.uniq(values, function(v){ return v.name }).length == values.length
}
With vanilla JavaScript
By checking if there is no recurring names in the array.
function isNameUnique(values){
var names = values.map(function(v){ return v.name });
return !names.some(function(v){
return names.filter(function(w){ return w==v }).length>1
});
}
//checking duplicate elements in an array
var arr=[1,3,4,6,8,9,1,3,4,7];
var hp=new Map();
console.log(arr.sort());
var freq=0;
for(var i=1;i<arr.length;i++){
// console.log(arr[i-1]+" "+arr[i]);
if(arr[i]==arr[i-1]){
freq++;
}
else{
hp.set(arr[i-1],freq+1);
freq=0;
}
}
console.log(hp);
You can use map to return just the name, and then use this forEach trick to check if it exists at least twice:
var areAnyDuplicates = false;
values.map(function(obj) {
return obj.name;
}).forEach(function (element, index, arr) {
if (arr.indexOf(element) !== index) {
areAnyDuplicates = true;
}
});
Fiddle
Adding updated es6 function to check for unique and duplicate values in array. This function is modular and can be reused throughout the code base. Thanks to all the post above.
/* checks for unique keynames in array */
const checkForUnique = (arrToCheck, keyName) => {
/* make set to remove duplicates and compare to */
const uniqueValues = [...new Set(arrToCheck.map(v => v[keyName]))];
if(arrToCheck.length !== uniqueValues.length){
console.log('NOT UNIQUE')
return false
}
return true
}
let arr = [{name:'joshua'},{name:'tony'},{name:'joshua'}]
/* call function with arr and key to check for */
let isUnique = checkForUnique(arr,'name')
checkDuplicate(arr, item) {
const uniqueValues = new Set(arr.map((v) => v[item]));
return uniqueValues.size < arr.length;
},
console.log(this.checkDuplicate(this.dutyExemptionBase, 'CI_ExemptionType')); // true || false
It is quite interesting to work with arrays
You can use new Set() method to find duplicate values!
let's assume you have an array of objects like this...
let myArray = [
{ id: 0, name: "Jhon" },
{ id: 1, name: "sara" },
{ id: 2, name: "pop" },
{ id: 3, name: "sara" }
]
const findUnique = new Set(myArray.map(x => {
return x.name
}))
if(findUnique.size < myArray.length){
console.log("duplicates found!")
}else{
console.log("Done!")
}
const duplicateValues = [{ name: "abc" }, { name: "bcv" }, { name: "abc" }];
const isContainDuplicate = (params) => {
const removedDuplicate = new Set(params.map((el) => el.name));
return params.length !== removedDuplicate.size;
};
const isDuplicate = isContainDuplicate(duplicateValues);
console.log("isDuplicate");

How can I check if the array of objects have duplicate property values?

I need some help with iterating through array, I keep getting stuck or reinventing the wheel.
values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName1' },
{ name: 'someName1' }
]
How could I check if there are two (or more) same name value in array? I do not need a counter, just setting some variable if array values are not unique. Have in mind that array length is dynamic, also array values.
Use array.prototype.map and array.prototype.some:
var values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName2' }
];
var valueArr = values.map(function(item){ return item.name });
var isDuplicate = valueArr.some(function(item, idx){
return valueArr.indexOf(item) != idx
});
console.log(isDuplicate);
ECMA Script 6 Version
If you are in an environment which supports ECMA Script 6's Set, then you can use Array.prototype.some and a Set object, like this
let seen = new Set();
var hasDuplicates = values.some(function(currentObject) {
return seen.size === seen.add(currentObject.name).size;
});
Here, we insert each and every object's name into the Set and we check if the size before and after adding are the same. This works because Set.size returns a number based on unique data (set only adds entries if the data is unique). If/when you have duplicate names, the size won't increase (because the data won't be unique) which means that we would have already seen the current name and it will return true.
ECMA Script 5 Version
If you don't have Set support, then you can use a normal JavaScript object itself, like this
var seen = {};
var hasDuplicates = values.some(function(currentObject) {
if (seen.hasOwnProperty(currentObject.name)) {
// Current name is already seen
return true;
}
// Current name is being seen for the first time
return (seen[currentObject.name] = false);
});
The same can be written succinctly, like this
var seen = {};
var hasDuplicates = values.some(function (currentObject) {
return seen.hasOwnProperty(currentObject.name)
|| (seen[currentObject.name] = false);
});
Note: In both the cases, we use Array.prototype.some because it will short-circuit. The moment it gets a truthy value from the function, it will return true immediately, it will not process rest of the elements.
In TS and ES6 you can create a new Set with the property to be unique and compare it's size to the original array.
const values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName3' },
{ name: 'someName1' }
]
const uniqueValues = new Set(values.map(v => v.name));
if (uniqueValues.size < values.length) {
console.log('duplicates found')
}
To know if simple array has duplicates we can compare first and last indexes of the same value:
The function:
var hasDupsSimple = function(array) {
return array.some(function(value) { // .some will break as soon as duplicate found (no need to itterate over all array)
return array.indexOf(value) !== array.lastIndexOf(value); // comparing first and last indexes of the same value
})
}
Tests:
hasDupsSimple([1,2,3,4,2,7])
// => true
hasDupsSimple([1,2,3,4,8,7])
// => false
hasDupsSimple([1,"hello",3,"bye","hello",7])
// => true
For an array of objects we need to convert the objects values to a simple array first:
Converting array of objects to the simple array with map:
var hasDupsObjects = function(array) {
return array.map(function(value) {
return value.suit + value.rank
}).some(function(value, index, array) {
return array.indexOf(value) !== array.lastIndexOf(value);
})
}
Tests:
var cardHand = [
{ "suit":"spades", "rank":"ten" },
{ "suit":"diamonds", "rank":"ace" },
{ "suit":"hearts", "rank":"ten" },
{ "suit":"clubs", "rank":"two" },
{ "suit":"spades", "rank":"three" },
]
hasDupsObjects(cardHand);
// => false
var cardHand2 = [
{ "suit":"spades", "rank":"ten" },
{ "suit":"diamonds", "rank":"ace" },
{ "suit":"hearts", "rank":"ten" },
{ "suit":"clubs", "rank":"two" },
{ "suit":"spades", "rank":"ten" },
]
hasDupsObjects(cardHand2);
// => true
if you are looking for a boolean, the quickest way would be
var values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName1' },
{ name: 'someName1' }
]
// solution
var hasDuplicate = false;
values.map(v => v.name).sort().sort((a, b) => {
if (a === b) hasDuplicate = true
})
console.log('hasDuplicate', hasDuplicate)
const values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName4' }
];
const foundDuplicateName = values.find((nnn, index) =>{
return values.find((x, ind)=> x.name === nnn.name && index !== ind )
})
console.log(foundDuplicateName)
Found the first one duplicate name
const values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName4' }
];
const foundDuplicateName = values.find((nnn, index) =>{
return values.find((x, ind)=> x.name === nnn.name && index !== ind )
})
You just need one line of code.
var values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName2' }
];
let hasDuplicates = values.map(v => v.name).length > new Set(values.map(v => v.name)).size ? true : false;
Try an simple loop:
var repeat = [], tmp, i = 0;
while(i < values.length){
repeat.indexOf(tmp = values[i++].name) > -1 ? values.pop(i--) : repeat.push(tmp)
}
Demo
With Underscore.js A few ways with Underscore can be done. Here is one of them. Checking if the array is already unique.
function isNameUnique(values){
return _.uniq(values, function(v){ return v.name }).length == values.length
}
With vanilla JavaScript
By checking if there is no recurring names in the array.
function isNameUnique(values){
var names = values.map(function(v){ return v.name });
return !names.some(function(v){
return names.filter(function(w){ return w==v }).length>1
});
}
//checking duplicate elements in an array
var arr=[1,3,4,6,8,9,1,3,4,7];
var hp=new Map();
console.log(arr.sort());
var freq=0;
for(var i=1;i<arr.length;i++){
// console.log(arr[i-1]+" "+arr[i]);
if(arr[i]==arr[i-1]){
freq++;
}
else{
hp.set(arr[i-1],freq+1);
freq=0;
}
}
console.log(hp);
You can use map to return just the name, and then use this forEach trick to check if it exists at least twice:
var areAnyDuplicates = false;
values.map(function(obj) {
return obj.name;
}).forEach(function (element, index, arr) {
if (arr.indexOf(element) !== index) {
areAnyDuplicates = true;
}
});
Fiddle
Adding updated es6 function to check for unique and duplicate values in array. This function is modular and can be reused throughout the code base. Thanks to all the post above.
/* checks for unique keynames in array */
const checkForUnique = (arrToCheck, keyName) => {
/* make set to remove duplicates and compare to */
const uniqueValues = [...new Set(arrToCheck.map(v => v[keyName]))];
if(arrToCheck.length !== uniqueValues.length){
console.log('NOT UNIQUE')
return false
}
return true
}
let arr = [{name:'joshua'},{name:'tony'},{name:'joshua'}]
/* call function with arr and key to check for */
let isUnique = checkForUnique(arr,'name')
checkDuplicate(arr, item) {
const uniqueValues = new Set(arr.map((v) => v[item]));
return uniqueValues.size < arr.length;
},
console.log(this.checkDuplicate(this.dutyExemptionBase, 'CI_ExemptionType')); // true || false
It is quite interesting to work with arrays
You can use new Set() method to find duplicate values!
let's assume you have an array of objects like this...
let myArray = [
{ id: 0, name: "Jhon" },
{ id: 1, name: "sara" },
{ id: 2, name: "pop" },
{ id: 3, name: "sara" }
]
const findUnique = new Set(myArray.map(x => {
return x.name
}))
if(findUnique.size < myArray.length){
console.log("duplicates found!")
}else{
console.log("Done!")
}
const duplicateValues = [{ name: "abc" }, { name: "bcv" }, { name: "abc" }];
const isContainDuplicate = (params) => {
const removedDuplicate = new Set(params.map((el) => el.name));
return params.length !== removedDuplicate.size;
};
const isDuplicate = isContainDuplicate(duplicateValues);
console.log("isDuplicate");

Categories