Extract subarray that share same values - javascript

I am looking for some help in relation to the next code:
var items = [
{ name: 'Edward', value: 21 },
{ name: 'Sharpe', value: 37 },
{ name: 'And', value: 45 },
{ name: 'The', value: -12 },
{ name: 'Magnetic', value: 37 },
{ name: 'Zeros', value: 37 }
];
// Sort by value
items.sort(function (a, b) {
return a.value - b.value;
});
So, after I sort the items of the array in ascending or descending order, I want to extract the sub-array of objects that share the same value. Is there an easy approach for this in javascript?
The output related to previous example should be like:
[
{ name: 'Magnetic', value: 37 },
{ name: 'Sharpe', value: 37 },
{ name: 'Zeros', value: 37 }
]

Having the array of items sorted by value is good because you can take advantage of that and use Array.filter() to check if the previous item or the next one share the same value with the current inspected item on each iteration:
var items = [
{name: 'Edward', value: 21},
{name: 'Sharpe', value: 37},
{name: 'And', value: 45},
{name: 'The', value: -12},
{name: 'Magnetic', value: 37},
{name: 'Zeros', value: 37},
{name: 'foo', value: 21}
];
// Sort by value.
items.sort((a, b) => a.value - b.value);
console.log("Sorted items:", items);
// Filter elements that share same value.
let onlyDups = items.filter((o, i, arr) =>
{
return (i - 1 >= 0 && (arr[i - 1].value === o.value)) ||
(i + 1 < arr.length && arr[i + 1].value === o.value);
});
console.log("Items that share value:", onlyDups);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}

You can use javascript Filter
items.filter((i)=>{
let duplicate = items.filter((x)=> {
return x.value === i.value;
})
// Return if has duplicate
return duplicate.length > 1
})
output:
[{ name: 'Magnetic', value: 37 }, { name: 'Sharpe', value: 37 }, { name: 'Zeros', value: 37 }]

Related

JS sort array of objets by specific string property (not ascending or descending)

do you know if there is a way to sort an array of objects using the sort() method for a specific property? In my case I'd like to sort my array first for "bananas", then for "pears", and then the rest.
const initialData = [
{ name: "strawberries", value: 12 },
{ name: "bananas", value: 3 },
{ name: "pears", value: 8 },
{ name: "pears", value: 7 },
{ name: "bananas", value: 10 },
{ name: "apples", value: 6 },
{ name: "bananas", value: 13 },
{ name: "bananas", value: 5 }
]
This is how I'd like the sorted data to look like:
const sortedData = [
{ name: "bananas", value: 3 },
{ name: "bananas", value: 10 },
{ name: "bananas", value: 13 },
{ name: "bananas", value: 5 },
{ name: "pears", value: 8 },
{ name: "pears", value: 7 },
{ name: "strawberries", value: 12 },
{ name: "apples", value: 6 }
]
I know that this function would sort my data ascending or descending:
initialData.sort(function(a, b) {
if (a.name < b.name) {
return -1;
}
if (a.name > b.name) {
return 1;
}
return 0;
});
You could take an object with the wanted order and take a large value as default for sorting.
const
data = [{ name: "strawberries", value: 12 }, { name: "bananas", value: 3 }, { name: "pears", value: 8 }, { name: "pears", value: 7 }, { name: "bananas", value: 10 }, { name: "apples", value: 6 }, { name: "bananas", value: 13 }, { name: "bananas", value: 5 }],
order = { bananas: 1, pears: 2 };
data.sort((a, b) => (order[a.name] || Number.MAX_VALUE) - (order[b.name] || Number.MAX_VALUE));
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
you can use an auxiliary array to define the sorting scheme. Here is an example:
const sortingScheme = [
"bananas",
"pears",
"strawberries",
"apples"
]
initialData.sort((a, b) => {
const indexOfa = sortingScheme.indexOf(a.name);
const indexOfb = sortingScheme.indexOf(b.name);
if (indexOfa > indexOfb)
return 1
if (indexOfa < indexOfb)
return -1
return 0;
})
This only works if you know all the names the array can hold. If you are interested in ordering only a few fields you can do this:
const orderedScheme = [
"bananas",
"pears"
]
initialData.sort((a, b) => {
let indexOfa = orderedScheme.indexOf(a.name);
let indexOfb = orderedScheme.indexOf(b.name);
if (indexOfa < 0) indexOfa = orderedScheme.length;
if (indexOfb < 0) indexOfb = orderedScheme.length;
if (indexOfa > indexOfb)
return 1
if (indexOfa < indexOfb)
return -1
return 0;
})
This is because if the element is not contained in the 'sortingScheme' array, the indexOf() function returns -1 which would place the element at the beginning of the array.
I hope I have been useful to you!
You Can Use This For Custom Sorting:
initialData.sort(function(a, b) {
var customSort="bpsacdefghijklmnoqrtuvwxyz";
var aa=customSort.indexOf(a.substr(0,1));
var bb=customSort.indexOf(b.substr(0,1));
if (aa < bb) {
return -1;
}else if (aa > bb) {
return 1;
}else
return 0;
});
Sorting Performs According to This Characters:
var customSort="bpsacdefghijklmnoqrtuvwxyz";
sort method, check for names. when names are equal order by value. When names not equal then order by names. (check inline comments)
const initialData = [
{ name: "strawberries", value: 12 },
{ name: "bananas", value: 3 },
{ name: "pears", value: 8 },
{ name: "pears", value: 7 },
{ name: "bananas", value: 10 },
{ name: "apples", value: 6 },
{ name: "bananas", value: 13 },
{ name: "bananas", value: 5 },
];
initialData.sort((a, b) => {
const order = ["pears", "bananas"]; // high priority items towards end.
if (a.name === b.name && order.includes(a.name)) {
return a.value - b.value;
} else {
// when item not in order, indexOf will return -1.
// Basically comparing the values -1, 0, 1
return order.indexOf(b.name) - order.indexOf(a.name);
}
});
console.log(initialData);
you can to this
const initialData = [
{ name: "strawberries", value: 12 },
{ name: "bananas", value: 3 },
{ name: "pears", value: 8 },
{ name: "pears", value: 7 },
{ name: "bananas", value: 10 },
{ name: "apples", value: 6 },
{ name: "bananas", value: 13 },
{ name: "bananas", value: 5 }
]
const data = initialData.sort(function(a, b) {
if (a.name < b.name) {
if(a.name === "apples") {
return 1
}
return -1;
}
return 0;
});
console.log(data)

How to get the 3 objects with a larger value than the other elements of the array javascript [duplicate]

This question already has answers here:
Get top three objects by object property in array [duplicate]
(1 answer)
JS - Get top 5 max elements from array
(5 answers)
Sorting an array of objects by property values
(35 answers)
Closed 3 years ago.
I have an array made up of objects. Each object has two properties: name, value.
array = [
{
name: 'name1',
value: 0
},
{
name: 'name2',
value: 2
},
{
name: 'name3',
value: 4
},
{
name: 'name4',
value: 4
},
{
name: 'name5',
value: 3
},
{
name: 'name6',
value: 2
},
{
name: 'name7',
value: 0
},
{
name: 'name8',
value: 1
},
...
]
How do I get objects with the highest value property?
In the above example I should restitute objects that have value = 4, value = 3, value = 2 (i.e. the first 3 largest values)
i have tried something like this:
let first: 0
let second: 0
let third: 0
array.map((res: any) => {
if (res.value > first) {
third = second
second = first
first = res
} else if (res.value > second) {
third = second
second = res
} else if (res.value > third) {
third = res
}
})
The problem is that if there are two or more objects with the same value, it does not return them both, but only one.
You need to sort the array first and then find all the elements containing top 3 values. For that you might need to keep an array checking if you have pushed all the elements containing the same value or not.
var pickedValues = [];
array = array
.sort((a, b) => a.value > b.value ? -1 : 1)
.filter(el => {
if(pickedValues.length === 3 && !pickedValues.includes(el.value)) return false;
if(!pickedValues.includes(el.value)) pickedValues.push(el.value);
return true;
});
Check the working code below:
var array = [{
name: 'name1',
value: 0
},
{
name: 'name2',
value: 2
},
{
name: 'name3',
value: 4
},
{
name: 'name4',
value: 4
},
{
name: 'name5',
value: 3
},
{
name: 'name6',
value: 2
},
{
name: 'name7',
value: 0
},
{
name: 'name8',
value: 1
},
];
var pickedValues = [];
array = array
.sort((a, b) => a.value > b.value ? -1 : 1)
.filter(el => {
if(pickedValues.length === 3 && !pickedValues.includes(el.value)) return false;
if(!pickedValues.includes(el.value)) pickedValues.push(el.value);
return true;
});
console.log(array)
Here is how you do it in detail.
let array = [
{
name: 'name1',
value: 0
},
{
name: 'name2',
value: 2
},
{
name: 'name3',
value: 4
},
{
name: 'name4',
value: 4
},
{
name: 'name5',
value: 3
},
{
name: 'name6',
value: 2
},
{
name: 'name7',
value: 0
},
{
name: 'name8',
value: 1
}];
let sorted = array.sort(function(a, b) {
if(a.value > b.value) { return -1 };
if(a.value < b.value) { return 1 };
return 0;
});
console.table(array.splice(0, 3));
First you need to sort the array, you can use the default sort method, why we have taken the copy using spread syntax is basically we don't want to mutate the existing array.
Once you sort you can do a slice which will give you the required elements.
See the working code snippet
let array = [
{
name: 'name1',
value: 0
},
{
name: 'name2',
value: 2
},
{
name: 'name3',
value: 4
},
{
name: 'name4',
value: 4
},
{
name: 'name5',
value: 3
},
{
name: 'name6',
value: 2
},
{
name: 'name7',
value: 0
},
{
name: 'name8',
value: 1
}
]
let sortedArray = [...array].sort((a,b) => b.value - a.value)
// now the array is sorted on descending now you can choose the first three or how much i require
console.log(sortedArray.slice(0,3))

Intersection of multiple arrays base on properties

I want to find the common elements of multiple array of objects based on a common property. In addition, if an element appears more than once, I want the resulting array to reflect the number of times it occurs in all the arrays.
I tried the following:
var arr = [
[
{ name: 'kiwi', value: 12 },
{ name: 'apple', value: 5 },
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'fish', value: 5 },
{ name: 'milk', value: 5 },
{ name: 'banana', value: 7 },
{ name: 'orange', value: 11 },
],
[
{ name: 'taco', value: 23 },
{ name: 'pizza', value: 78 },
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'fish', value: 5 },
{ name: 'pie', value: 1 },
{ name: 'cake', value: 3 },
{ name: 'banana', value: 7 },
{ name: 'beef', value: 123 },
{ name: 'lime', value: 72 },
{ name: 'pizza', value: 34 },
],
[
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'pizza', value: 23 },
{ name: 'fish', value: 5 },
{ name: 'banana', value: 7 },
{ name: 'banana', value: 77 },
]
];
function findArraysWithCommonName(arr) {
let arrays = [...arr];
var result = arrays.shift().reduce(function(res, v) {
if (arrays.every(function(a) {
return (a.filter(function(e) {
return e.name === v.name
}).length > 0);
})) res.push(v);
return res;
}, []);
return result;
}
console.log(findArraysWithCommonName(arr))
The result I got is:
[
{name: "apple", value: 5},
{name: "apple", value: 12},
{name: "pizza", value: 33},
{name: "pizza", value: 24},
{name: "fish", value: 5},
{name: "banana", value: 7}
]
I expect the output to be:
[
{name: "apple", value: 12},
{name: "pizza", value: 33},
{name: "pizza", value: 24},
{name: "fish", value: 5},
{name: "banana", value: 7}
]
or
[
{name: "apple", value: 5},
{name: "pizza", value: 33},
{name: "pizza", value: 24},
{name: "fish", value: 5},
{name: "banana", value: 7}
]
One approach would be to build a map that relates an object to it's "count" in the array (ie the number of times that object occours in arr).
This can be done via .reduce() where you serialize each object to a string via JSON.stringify(obj) - this string is a unique encoding of the corresponding object shape and state which is used as the key to identify the objects of this form in the mapping. The key is used to query and update the "count" value of the mapping, for each object encountered in the arr.
Once the mapping has been build, filter mapping entries by those with a "count" value greater than one.
Finally for any filtered entries, deserialize the corresponding keys of those entries via .map() to obtain an array of objects that occoured more that one in the original arr.
This approach could be implemented as:
var arr=[[{name:'kiwi',value:12},{name:'apple',value:5},{name:'apple',value:12},{name:'pizza',value:33},{name:'pizza',value:24},{name:'fish',value:5},{name:'milk',value:5},{name:'banana',value:7},{name:'orange',value:11}],[{name:'taco',value:23},{name:'pizza',value:78},{name:'apple',value:12},{name:'pizza',value:33},{name:'pizza',value:24},{name:'fish',value:5},{name:'pie',value:1},{name:'cake',value:3},{name:'banana',value:7},{name:'beef',value:123},{name:'lime',value:72},{name:'pizza',value:34}],[{name:'apple',value:12},{name:'pizza',value:33},{name:'pizza',value:24},{name:'pizza',value:23},{name:'fish',value:5},{name:'banana',value:7},{name:'banana',value:77}]];
/* Flatten array heirachy */
const flatArr = arr.flat();
/* Obtain a count mapping for each object's occourance in flatArr */
const mapObjectToCount = flatArr.reduce((map, item) => {
const key = JSON.stringify(item);
const count = (map[key] ? map[key] : 0) + 1;
return { ...map, [ key ] : count };
}, {})
/* Get key/value pair of the prior mapping, filter the objects by
those that occour more that one time, and obtain the original object
by parsing the key */
const result = Object.entries(mapObjectToCount)
.filter(([json, count]) => count > 1)
.map(([json]) => JSON.parse(json));
console.log(result)
I'd first transform each subarray into an object indexed by the number of occurences of each name. Then, iterate through each of those sub-objects created, creating a new object whose values are the minimum of the values found on the combined object, for every key.
Lastly, return a .filter of the first array, checking whether the occurence count of the name being iterated over on that object is greater than 0, reducing that count by one when found:
function findArraysWithCommonName(arr) {
const [oneArr, ...rest] = arr;
/* Transform each subarray into, eg:
{
"taco": 1,
"pizza": 4,
"apple": 1,
"fish": 1,
"pie": 1,
...
*/
const countsByName = rest.map(
subarr => subarr.reduce((a, { name }) => {
a[name] = (a[name] || 0) + 1;
return a;
}, {})
);
/* Combine the objects into one that contains only the minimum value for each property, eg:
{
"apple": 1,
"pizza": 3,
"fish": 1,
"banana": 1
}
*/
const combinedCountsByName = countsByName.reduce((a, countObj) => {
Object.entries(countObj).forEach(([key, val]) => {
countObj[key] = Math.min(a[key], val) || 0;
});
return countObj;
});
console.log(combinedCountsByName);
return oneArr.filter(({ name }) => {
if (combinedCountsByName[name] > 0) {
combinedCountsByName[name]--;
return true;
}
});
}
var arr = [
[
{ name: 'kiwi', value: 12 },
{ name: 'apple', value: 5 },
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'fish', value: 5 },
{ name: 'milk', value: 5 },
{ name: 'banana', value: 7 },
{ name: 'orange', value: 11 },
],
[
{ name: 'taco', value: 23 },
{ name: 'pizza', value: 78 },
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'fish', value: 5 },
{ name: 'pie', value: 1 },
{ name: 'cake', value: 3 },
{ name: 'banana', value: 7 },
{ name: 'beef', value: 123 },
{ name: 'lime', value: 72 },
{ name: 'pizza', value: 34 },
],
[
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'pizza', value: 23 },
{ name: 'fish', value: 5 },
{ name: 'banana', value: 7 },
{ name: 'banana', value: 77 },
]
];
console.log(findArraysWithCommonName(arr));

Javascript - Group by one property of object in an array of objects [duplicate]

This question already has answers here:
Group by/order by On JSON data using javascript/jquery
(3 answers)
Closed 3 years ago.
I know that, there are countless of questions about group by one property from an array of object. But what I want to do is a litte more specific:
const lists = [
{ groupKey: 'ABC', key: 'r8', timestamp: '2014', index: 2 },
{ groupKey: 'ABC', key: 'q8', timestamp: '2012', index: 0 },
{ groupKey: 'ABC', key: 'w8', timestamp: '2013', index: 1 },
{ groupKey: 'CDE', key: 'r7', timestamp: '2019', index: 0 }
]
Result should be grouped by groupKey and sorted by index
(indexes here are iterators 0,1,2,3... so no need to actually be sorted but rather to be placed in a right order of an array. Example: array[index] = ... ).
This should look like:
{
ABC: [
{ key: 'q8', timestamp: '2012', index: 0 },
{ key: 'w8', timestamp: '2013', index: 1 },
{ key: 'r8', timestamp: '2014', index: 2 }
],
CDE: [
{ key: 'r7', timestamp: '2019', index: 0 }
]
}
I have tried to group by without sorting:
const result = lists.reduce((r, item) => {
let { groupKey, ...rest } = item
r[item.groupKey] = [...(r[item.groupKey] || []), rest]
return r
}, {})
And with sorting, not successful but you know what I mean:
const result = lists.reduce((r, item) => {
let { groupKey, ...rest } = item
r[item.groupKey][item.index] = rest //err: can not set property 2 of undefined
return r
}, {})
Any suggestions are appreciated
You could take the index directly without later sorting.
const
lists = [{ groupKey: 'ABC', key: 'r8', timestamp: '2014', index: 2 }, { groupKey: 'ABC', key: 'q8', timestamp: '2012', index: 0 }, { groupKey: 'ABC', key: 'w8', timestamp: '2013', index: 1 }, { groupKey: 'CDE', key: 'r7', timestamp: '2019', index: 0 }],
result = lists.reduce((r, { groupKey, ...rest }) => {
r[groupKey] = r[groupKey] || [];
r[groupKey][rest.index] = rest;
return r
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You're almost there, you just need to sort after the reduce is finished:
const lists = [{
groupKey: 'ABC',
key: 'r8',
timestamp: '2014',
index: 2
},
{
groupKey: 'ABC',
key: 'q8',
timestamp: '2012',
index: 0
},
{
groupKey: 'ABC',
key: 'w8',
timestamp: '2013',
index: 1
},
{
groupKey: 'CDE',
key: 'r7',
timestamp: '2019',
index: 0
}
]
const result = lists.reduce((r, item) => {
const { groupKey, ...rest } = item
r[groupKey] = [...(r[groupKey] || []), rest];
return r;
}, {})
Object.values(result).forEach((arr) => {
arr.sort((a, b) => a.index - b.index);
});
console.log(result);
You can first group by keys and then you need to sort based on index
const lists = [{ groupKey: 'ABC', key: 'r8', timestamp: '2014', index: 2 },{ groupKey: 'ABC', key: 'q8', timestamp: '2012', index: 0 }, { groupKey: 'ABC', key: 'w8', timestamp: '2013', index: 1 },{ groupKey: 'CDE', key: 'r7', timestamp: '2019', index: 0 } ]
// group by groupkey
let grouped = lists.reduce((op, inp) => {
let groupKey = inp.groupKey
op[groupKey] = op[groupKey] || []
op[groupKey].push(inp)
return op
},{})
// sort by index
let output = Object.entries(grouped).reduce((op,[key,value]) => {
value = value.sort((a,b)=> a.index-b.index)
op[key] = value
return op
},{})
console.log(output)
Try like this
const lists = [
{ groupKey: 'ABC', key: 'r8', timestamp: '2014', index: 2 },
{ groupKey: 'ABC', key: 'q8', timestamp: '2012', index: 0 },
{ groupKey: 'ABC', key: 'w8', timestamp: '2013', index: 1 },
{ groupKey: 'CDE', key: 'r7', timestamp: '2019', index: 0 }
]
var groupBy = function(datas, key) {
return datas.reduce(function(data, x) {
var dKey = x[key];
delete x[key];
(data[dKey] = data[dKey] || []).push(x);
return data;
}, {});
};
console.log(groupBy(lists, 'groupKey'));
Try this:
const result = lists.reduce((r, item) => {
let { groupKey, ...rest } = item
r[item.groupKey] = [...(r[item.groupKey] || []), rest];
(r[item.groupKey]).sort((a, b) => a.index - b.index);
return r
}, {});

Sort by two properties conditionally [duplicate]

This question already has answers here:
How to sort an array of objects by multiple fields?
(38 answers)
Closed 4 years ago.
Consider the following scenario, where I have to sort a list of students by both name and scores.
[
{
name: 'Max',
score: 94
},
{
name: 'Jerome',
score: 86
},
{
name: 'Susan',
score: 86
},
{
name: 'Abel',
score: 86
},
{
name: 'Kevin',
score: 86
}
]
I want to sort the list by the student who scored the highest, but if two or more students have the same score, then I want to sort those students alphabetically. For the above case, the result should be as below:
[
{
name: 'Max',
score: 94
},
{
name: 'Abel',
score: 86
},
{
name: 'Jerome',
score: 86
},
{
name: 'Kevin',
score: 86
},
{
name: 'Susan',
score: 86
}
]
How can I achieve this? Is there any lodash function that I can use, or is it possible with pure JavaScript?
No library needed, just compare the scores, and if that comes out to 0, compare the names:
const arr=[{name:'Max',score:94},{name:'Jerome',score:86},{name:'Susan',score:86},{name:'Abel',score:86},{name:'Kevin',score:86}]
console.log(
arr.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name))
);
Try the following:
var arr = [
{
name: 'Max',
score: 94
},
{
name: 'Jerome',
score: 86
},
{
name: 'Susan',
score: 86
},
{
name: 'Abel',
score: 86
},
{
name: 'Kevin',
score: 86
}
];
arr.sort((a,b)=>{
return (b.score - a.score) || a.name.localeCompare(b.name);
});
console.log(arr);
You could sort by _.sortBy and take a list of propertis and another for the order.
var array = [{ name: 'Max', score: 94 }, { name: 'Jerome', score: 86 }, { name: 'Susan', score: 86 }, { name: 'Abel', score: 86 }, { name: 'Kevin', score: 86 }];
console.log(_.sortBy(array, ['score', 'name'], ['desc', 'asc']))
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
First sort the array by score and if both scores are equal then sort it by name. Consider below code for the same:
let obj = [
{
name: 'Max',
score: 94
},
{
name: 'Jerome',
score: 86
},
{
name: 'Susan',
score: 86
},
{
name: 'Abel',
score: 86
},
{
name: 'Kevin',
score: 86
}
];
obj.sort((a,b) => {
if(b.score - a.score === 0){
if(a.name < b.name) return -1;
if(a.name > b.name) return 1;
return 0;
}
else{
return b.score - a.score;
}
});
console.log(obj);

Categories