Related
I have two deal with two Set instances.
const set1 = new Set([
{ name: 'a' },
{ name: 'b', lastname: 'bb' },
{ name: 'c' },
{ name: 'd' },
]);
const set2 = new Set([
{ name: 'b' },
{ name: 'd' },
]);
Any object within a set will feature several and also distinct keys and values. The goal is to find structurally equal objects (same keys and values) in both sets, which is ... The intersection of equal data items in/of set1 and set2.
In the following example the expected result is [ { name: 'd' } ] ...
console.log([...set1].filter(item => set2.has(item)));
... but it logs an empty array / [] instead.
An object features more than 20 keys so one has to compare them one by one, which can not be done in a hard coded way.
How could one achieve a generic approach for an intersection of two lists of structurally equal data items?
You can do something like this:
const set1 = new Set([
{name: 'a'},
{name: 'b', lastname: 'bb'},
{name: 'c'},
{name: 'd'}
]);
const set2 = new Set([
{name: 'b'},
{name: 'd'}
]);
set1.forEach((value) => {
if (![...set2].some((o) => Object.entries(o).every(([k, v], _, arr) => (Object.keys(value).length === arr.length && value[k] === v)))) {
set1.delete(value);
}
})
console.log([...set1]);
What this does, is to iterate through set1 and if the item at the current iteration is not the same as any item in set2 (![...set2].some(..)), it is deleted.
The items are considered the same if they have the same number of keys and if the values at the same key are strictly equal.
This only works if the values of the objects in the sets are primitives, if they are not, you'll have to change value[k] === v to an appropriate comparison.
One could write a generic solution which compares pure, thus JSON conform, data structures regardless of any object's nesting depth/level and (creation time) key order.
Such a function would be self recursive for Array item (order matters) and Object property (key order does not matter) comparison. Otherwise values are compared strictly.
function isDeepDataStructureEquality(a, b) {
let isEqual = Object.is(a, b);
if (!isEqual) {
if (Array.isArray(a) && Array.isArray(b)) {
isEqual = (a.length === b.length) && a.every(
(item, idx) => isDeepDataStructureEquality(item, b[idx])
);
} else if (
a && b
&& (typeof a === 'object')
&& (typeof b === 'object')
) {
const aKeys = Object.keys(a);
const bKeys = Object.keys(b);
isEqual = (aKeys.length === bKeys.length) && aKeys.every(
(key, idx) => isDeepDataStructureEquality(a[key], b[key])
);
}
}
return isEqual;
}
const objA = { // `objA` equals `objB`.
name: 'foo',
value: 1,
obj: {
z: 'z',
y: 'y',
a: {
name: 'bar',
value: 2,
obj: {
x: 'x',
w: 'w',
b: 'b',
},
arr: ['3', 4, 'W', 'X', {
name: 'baz',
value: 3,
obj: {
k: 'k',
i: 'i',
c: 'c',
},
arr: ['5', 6, 'B', 'A'],
}],
},
},
arr: ['Z', 'Y', 1, '2'],
};
const objB = { // `objB` equals `objA`.
arr: ['Z', 'Y', 1, '2'],
obj: {
z: 'z',
y: 'y',
a: {
obj: {
x: 'x',
w: 'w',
b: 'b',
},
arr: ['3', 4, 'W', 'X', {
obj: {
k: 'k',
i: 'i',
c: 'c',
},
name: 'baz',
value: 3,
arr: ['5', 6, 'B', 'A'],
}],
name: 'bar',
value: 2,
},
},
name: 'foo',
value: 1,
};
const objC = { // `objC` equals neither `objA` nor `objB`.
arr: ['Z', 'Y', 1, '2'],
obj: {
z: 'z',
y: 'y',
a: {
obj: {
x: 'x',
w: 'w',
b: 'b',
},
arr: ['3', 4, 'W', 'X', {
obj: {
k: 'k',
i: 'i',
c: 'C', // the single difference to `objA` and `objB`.
},
name: 'baz',
value: 3,
arr: ['5', 6, 'B', 'A'],
}],
name: 'bar',
value: 2,
},
},
name: 'foo',
value: 1,
};
console.log(
'isDeepDataStructureEquality(objA, objB) ?..',
isDeepDataStructureEquality(objA, objB)
);
console.log(
'isDeepDataStructureEquality(objA, objC) ?..',
isDeepDataStructureEquality(objA, objC)
);
console.log(
'isDeepDataStructureEquality(objB, objC) ?..',
isDeepDataStructureEquality(objB, objC)
);
console.log(
'isDeepDataStructureEquality(objB, objA) ?..',
isDeepDataStructureEquality(objB, objA)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Based on the above implementation of isDeepDataStructureEquality one can solve the OP's task, that actually looks for the intersection of two list structures, by additionally providing a getIntersectionOfDeeplyEqualDataStructures functionality ...
function getIntersectionOfDeeplyEqualDataStructures(a, b) {
return [...(a ?? [])]
.reduce((collector, sourceItem) => {
const { target, intersection } = collector;
const targetIndex = target.findIndex(targetItem =>
isDeepDataStructureEquality(targetItem, sourceItem)
);
if (targetIndex >= 0) {
// collect the intersection of
// both, source (a) and target (b).
intersection.push(target[targetIndex]);
}
return collector;
}, {
target: [...(b ?? [])],
intersection: [],
}).intersection;
}
const set1 = new Set([
{ name: 'a' },
{ name: 'b', lastname: 'bb' },
{ name: 'c' },
{ name: 'd' }
]);
const set2 = new Set([
{ name: 'b' },
{ name: 'd' },
]);
console.log(
"getIntersectionOfDeeplyEqualDataStructures(set1, set2) ...",
getIntersectionOfDeeplyEqualDataStructures(set1, set2)
);
const set3 = new Set([
{ name: 'a' },
{ name: 'b', lastname: 'bb' },
{ name: 'c' },
{
name: 'd',
list: ['foo', 1, null, false, 0, {
foo: { bar: { baz: 'bizz', buzz: '' } }
}],
},
]);
const set4 = new Set([
{
list: ['foo', 1, null, false, 0, {
foo: { bar: { buzz: '', baz: 'bizz' } }
}],
name: 'd',
},
{ name: 'C' },
{ lastname: 'bb', name: 'b' },
{ name: 'aa' }
]);
console.log(
"getIntersectionOfDeeplyEqualDataStructures(set3, set4) ...",
getIntersectionOfDeeplyEqualDataStructures(set3, set4)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
function isDeepDataStructureEquality(a, b) {
let isEqual = Object.is(a, b);
if (!isEqual) {
if (Array.isArray(a) && Array.isArray(b)) {
isEqual = (a.length === b.length) && a.every(
(item, idx) => isDeepDataStructureEquality(item, b[idx])
);
} else if (
a && b
&& (typeof a === 'object')
&& (typeof b === 'object')
) {
const aKeys = Object.keys(a);
const bKeys = Object.keys(b);
isEqual = (aKeys.length === bKeys.length) && aKeys.every(
(key, idx) => isDeepDataStructureEquality(a[key], b[key])
);
}
}
return isEqual;
}
</script>
Edit
As for Titus' approach ...
set1.forEach(value => {
if (
![...set2].some(o =>
Object.entries(o).every(([k, v], _, arr) =>
(Object.keys(value).length === arr.length && value[k] === v)
)
)
) {
set1.delete(value);
}
});
... which works for flat objects only, though already agnostic to key insertion order, one could optimize the code by ...
... not creating the keys array of the most outer currently processed object again and again with every nested some and every iteration.
thus, something like ... const valueKeys = Object.keys(value); ... before the if clause, already helps improving the code.
... inverting the nested some and every logic which does result in a more efficient way of ... deleting every flat data-item from the processed set which does not equal any flat data-item from the secondary set.
On top of that, one could implement a function statement which not only helps code-reuse but also makes the implementation independent from outer scope references.
For instance, the primary set which is operated and going to be mutated can be accessed as such a function's third parameter. But most important for outer scope independency is the also available thisArg binding for any set's forEach method. Thus any function statement or function expression can access e.g. the other/secondary set via this in case the latter was passed as the forEach's 2nd parameter.
Also an improved wording supports a better readability of the code ...
//the function naming of cause is exaggerated.
function deleteItemFromSourceWhichDoesNotEqualAnyItemFromBoundTarget(sourceItem, _, sourceSet) {
const targetSet = this;
const sourceKeys = Object.keys(sourceItem);
if (
// ... for any data-item from the (bound) target-set ...
[...targetSet].every(targetItem =>
// ... which does not equal the currently processed data-item from the source-set ...
Object.entries(targetItem).some(([targetKey, targetValue], _, targetEntries) =>
sourceKeys.length !== targetEntries.length || sourceItem[targetKey] !== targetValue
)
)
) {
// ... delete the currently processed data-item from the source-set.
sourceSet.delete(sourceItem);
}
}
const set1 = new Set([
{ name: 'a' }, // - to be kept.
{ name: 'b', lastname: 'bb' }, // - to be kept.
{ name: 'c' }, // - to be deleted.
{ name: 'd', nested: { name: 'a' } }, // - to be kept, but fails ...
]); // ... due to not being flat.
const set2 = new Set([
{ name: 'd', nested: { name: 'a' } }, // - should equal, but doesn't.
{ name: 'a' }, // - does equal.
{ lastname: 'bb', name: 'b' }, // - does equal.
{ name: 'e' }, // - doesn't equal.
]);
// `set1` is going to be mutated.
set1.forEach(deleteItemFromSourceWhichDoesNotEqualAnyItemFromBoundTarget, set2);
console.log(
'mutated `set1` now (almost) being equal to the intersection of initial `set1` and `set2` ...',
[...set1]
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
const set1 = new Set([
{name: 'a'},
{name: 'b', lastname: 'bb'},
{name: 'c'},
{name: 'd'}
]);
const set2 = new Set([
{name: 'b'},
{name: 'd'}
]);
const names = [...set2].map(s2 => s2.name);
console.log([...set1].filter(item => names.includes(item.name)));
const set1 = new Set([
{name: 'a'},
{name: 'b', lastname: 'bb'},
{name: 'c'},
{name: 'd'},
{name: 'e'}
]);
const set2 = new Set([
{name: 'c', lastname: 'ccc'},
{name: 'd'},
{name: 'b', lastname: 'cc'},
{name: 'e'}
]);
console.log([...set1].filter(item => {
const s2Arr = [...set2];
const itemKeys = Object.keys(item);
for(let i = 0; i < s2Arr.length; i++){
const s2Obj = s2Arr[i];
const s2ObjKeys = Object.keys(s2Obj);
if(s2ObjKeys.length == itemKeys.length){
let oneSame = true;
for(let j = 0; j < s2ObjKeys.length; j++){
const s2ObjKey = s2ObjKeys[j];
if(item[s2ObjKey] != s2Obj[s2ObjKey]){
oneSame = false;
}
}
if(oneSame)
return true;
}
}
return false;
}));
How to check if array contains different values with React.js and typescript?
Example:
[{
name: 'John',
value: 1,
}, {
name: 'John',
value: 1,
}, {
name: 'Carla',
value: 15,
}]
I want to return false if all objects in array are same, and true if there is at least one different object.
You can't use a direct equality comparison since objects will never return equal.
Ie {} != {}, and {name: 'John', value: 1} != {name: 'John', value: 1}.
So firstly you have to decide what you're going to define as 'equal' for these objects.
Let's say for the sake of this that you use just the name field as the test for equality. So if two objects in the array have the same name field, then you'll call them equal. Then you'd define the function:
type NameValue = {name: string, value: string}
const areEqual = (obj1: NameValue, obj2: NameValue): boolean => obj1.name === obj2.name
Of course you can change this function to reflect whatever you define as 'equal'. There are npm packages to help you with deep equality checks too, or you can JSON.stringify both and check that equality
Then you can use Array.some(). Array.some() will return true if any element in the array passes a test. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
Testing if any element is not equal to the first should be sufficient.
const areNotAllEqual = yourArray.some((currentElement) => {
return !areEqual(currentElement, yourArray[0])
})
After having commented on and criticized especially the approaches based on JSON.stringify, I want to contribute something on that matter. Since meanwhile all modern JS engines seem to be aware of an object's key order (in how this object was created) and also seem to guarantee such an order for key-iteration one could write a recursive function, which for any deeply nested but JSON-conform JS-objects reestablishes a normalized key-order for such objects but leaves arrays untouched.
Passing such key-normalized objects to JSON.stringify then makes such objects comparable by their's stringified signature ...
function defaultCompare(a, b) {
return ((a < b) && -1) || ((a > b) && 1) || 0;
}
function comparePropertyNames(a, b) {
return a.localeCompare
? a.localeCompare(b)
: defaultCompare(a, b);
}
function getJsonDataWithNormalizedKeyOrder(data) {
let value;
if (Array.isArray(data)) {
value = data.map(getJsonDataWithNormalizedKeyOrder);
} else if (data && (typeof data === 'object')) {
value = Object
.getOwnPropertyNames(data)
.sort(comparePropertyNames)
.reduce((obj, key) => {
obj[key] = getJsonDataWithNormalizedKeyOrder(data[key])
return obj;
}, {});
} else {
value = data;
}
return value;
}
const objA = {
name: 'foo',
value: 1,
obj: {
z: 'z',
y: 'y',
a: {
name: 'bar',
value: 2,
obj: {
x: 'x',
w: 'w',
b: 'b',
},
arr: ['3', 4, 'W', 'X', {
name: 'baz',
value: 3,
obj: {
k: 'k',
i: 'i',
c: 'c',
},
arr: ['5', 6, 'B', 'A'],
}],
},
},
arr: ['Z', 'Y', 1, '2'],
};
const objB = {
arr: ['Z', 'Y', 1, '2'],
obj: {
z: 'z',
y: 'y',
a: {
obj: {
x: 'x',
w: 'w',
b: 'b',
},
arr: ['3', 4, 'W', 'X', {
obj: {
k: 'k',
i: 'i',
c: 'c',
},
name: 'baz',
value: 3,
arr: ['5', 6, 'B', 'A'],
}],
name: 'bar',
value: 2,
},
},
name: 'foo',
value: 1,
};
const objC = {
arr: ['Z', 'Y', 1, '2'],
obj: {
z: 'z',
y: 'y',
a: {
obj: {
x: 'x',
w: 'w',
b: 'b',
},
arr: ['3', 4, 'W', 'X', {
obj: {
k: 'k',
i: 'i',
c: 'c',
},
name: 'baz',
value: 3,
arr: ['5', 6, 'B', 'A'],
}],
name: 'bar',
value: 2,
},
},
name: 'foo',
value: 2,
};
console.log(
'getJsonDataWithNormalizedKeyOrder(objA) ...',
getJsonDataWithNormalizedKeyOrder(objA)
);
console.log(
'getJsonDataWithNormalizedKeyOrder(objB) ...',
getJsonDataWithNormalizedKeyOrder(objB)
);
console.log(
'JSON.stringify(getJsonDataWithNormalizedKeyOrder(objA)) ...',
JSON.stringify(getJsonDataWithNormalizedKeyOrder(objA))
);
console.log(
'JSON.stringify(getJsonDataWithNormalizedKeyOrder(objB)) ...',
JSON.stringify(getJsonDataWithNormalizedKeyOrder(objB))
);
console.log(
'JSON.stringify(getJsonDataWithNormalizedKeyOrder(objC)) ...',
JSON.stringify(getJsonDataWithNormalizedKeyOrder(objC))
);
console.log(
'JSON.stringify(getJsonDataWithNormalizedKeyOrder(objA)).length ...',
JSON.stringify(getJsonDataWithNormalizedKeyOrder(objA)).length
);
console.log(
'JSON.stringify(getJsonDataWithNormalizedKeyOrder(objB)).length ...',
JSON.stringify(getJsonDataWithNormalizedKeyOrder(objB)).length
);
console.log(
'JSON.stringify(getJsonDataWithNormalizedKeyOrder(objC)).length ...',
JSON.stringify(getJsonDataWithNormalizedKeyOrder(objC)).length
);
console.log(`
JSON.stringify(
getJsonDataWithNormalizedKeyOrder(objA)
) === JSON.stringify(
getJsonDataWithNormalizedKeyOrder(objB)
) ?`,
JSON.stringify(
getJsonDataWithNormalizedKeyOrder(objA)
) === JSON.stringify(
getJsonDataWithNormalizedKeyOrder(objB)
)
);
console.log(`
JSON.stringify(
getJsonDataWithNormalizedKeyOrder(objA)
) === JSON.stringify(
getJsonDataWithNormalizedKeyOrder(objC)
) ?`,
JSON.stringify(
getJsonDataWithNormalizedKeyOrder(objA)
) === JSON.stringify(
getJsonDataWithNormalizedKeyOrder(objC)
)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Applying the above to an approach which solves the OP's original problem in a more generic way then might look similar to the next provided lines ...
function defaultCompare(a, b) {
return ((a < b) && -1) || ((a > b) && 1) || 0;
}
function comparePropertyNames(a, b) {
return a.localeCompare
? a.localeCompare(b)
: defaultCompare(a, b);
}
function getJsonDataWithNormalizedKeyOrder(data) {
let value;
if (Array.isArray(data)) {
value = data.map(getJsonDataWithNormalizedKeyOrder);
} else if (data && (typeof data === 'object')) {
value = Object
.getOwnPropertyNames(data)
.sort(comparePropertyNames)
.reduce((obj, key) => {
obj[key] = getJsonDataWithNormalizedKeyOrder(data[key])
return obj;
}, {});
} else {
value = data;
}
return value;
}
const sampleList = [{
name: 'John',
value: 1,
}, {
value: 1,
name: 'John',
}, {
name: 'Carla',
value: 15,
}];
function hasDifferentValues(arr) {
// stringified first item reference.
const referenceItem = JSON.stringify(getJsonDataWithNormalizedKeyOrder(arr[0]));
// run `some` from a sub-array which excludes the original array's first item.
return arr.slice(1).some(item =>
referenceItem !== JSON.stringify(getJsonDataWithNormalizedKeyOrder(item))
);
}
console.log(
'hasDifferentValues(sampleList) ?',
hasDifferentValues(sampleList)
);
console.log(
'hasDifferentValues(sampleList.slice(0,2)) ?',
hasDifferentValues(sampleList.slice(0,2))
);
console.log(
'hasDifferentValues(sampleList.slice(0,1)) ?',
hasDifferentValues(sampleList.slice(0,1))
);
console.log(
'hasDifferentValues(sampleList.slice(1)) ?',
hasDifferentValues(sampleList.slice(1))
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
I'd check whether stringified object array includes stringified item by referencing to a copied array where I remove the latest item. I'll use Array.every() to compare if all the items match together and then return the opposite value.
However, this can be very heavy operation if an object array is very lengthy
const arrSame = [{name: 1}, {name: 1}, {name: 1}];
const arrDiff = [{name:1}, {name: 2}, {name: 2}];
const arrDiff2 = [{name:1}, {name: 1}, {name: 2}];
const hasDifferentValues = (arr) => !arr.every((item, i, ref) => JSON.stringify([...ref].shift()).includes(JSON.stringify(item)));
console.log(hasDifferentValues(arrSame));
console.log(hasDifferentValues(arrDiff));
console.log(hasDifferentValues(arrDiff2));
This is not exactly react specific, but to check for differences you can iterate through the array using every like so.
const fooArray = [{
name: 'John',
value: 1,
nest: {
isValid: [1, 2]
}
},
{
value: 1,
name: 'John',
nest: {
isValid: [1, 1]
}
}, {
name: 'John',
value: 1,
nest: {
isValid: [1, 1]
}
}
]
// check each member against the last, see if there is a diff
const isSame = (element, index, arr) => {
if (index > 0) {
// https://stackoverflow.com/questions/1068834/object-comparison-in-javascript
// return JSON.stringify(element) === JSON.stringify(arr[index - 1])
// alternatively, you can check to see if some of the values are different
// by stringifying and checking if either are permuations of each other
// this is probably not the ideal way, but I added it for the sake of a different solution
const currentObStr = JSON.stringify(element).split("").sort().join()
const prevObStr = JSON.stringify(arr[index - 1]).split("").sort().join()
return currentObStr === prevObStr
}
return true
}
const everyElementIsSame = fooArray.every(isSame)
console.log(everyElementIsSame)
Here is my data:
const sectionDummy = [
{ floor: '1', data: ['A', 'B', 'C']},
{ floor: '2', data: ['D', 'E', 'F']},
{ floor: '3', data: ['G', 'H', 'I']},
];
I can filter floor and get the correct data:
let filterData = [];
filterData = sectionDummy.filter(item => {
if (item.floor !== undefined) {
return item.floor.trim().toLowerCase().indexOf(inputValue) >= 0;
}
return {};
});
If inputValue is 1, I will get the return data
console.log(filterData); // [{ floor: 1, data: ['A', 'B', 'C']}]
I'm stuck with the data array if I want to filter it too.
I try to use map
let filterData = [];
filterData = sectionDummy.filter(item => {
if (item.floor !== undefined) {
item.data.map((value, index) => {
if(value.trim().toLowerCase().indexOf(inputValue) >=0) {
return value;
} else {
return {};
}
});
}
return {};
});
It looks like filter and map data return is different, so it is not working.
How to filter both of floor and data array ?
Any help would be appreciated.
Do you want to filter by the floor and then the data?
const sectionDummy = [
{ floor: 1, data: ['A', 'B', 'C']},
{ floor: 2, data: ['D', 'E', 'F']},
{ floor: 3, data: ['G', 'H', 'I']},
];
const floorFilter = 2;
const dataFilter = 'E';
const filteredData = sectionDummy
.filter(item => item.floor === floorFilter)
.map(({floor, data}) => ({floor, data: data.filter(d => d === dataFilter)}));
console.log(filteredData); //[ { floor: 2, data: [ 'E' ] } ]
Or by the floor and by the data?
const sectionDummy = [
{ floor: 1, data: ['A', 'B', 'C']},
{ floor: 2, data: ['D', 'E', 'F']},
{ floor: 3, data: ['G', 'H', 'I']},
];
const floorFilter = 2;
const dataFilter = 'E';
const filteredData = sectionDummy
.filter(({floor, data}) => floor === floorFilter && data.some(d => d === dataFilter))
console.log(filteredData) // [ { floor: 2, data: [ 'D', 'E', 'F' ] } ]
You could filter by checking floor and inputValue and take the objects it the two values are equal.
let filterData = searchBuildings.filter(({ floor }) => floor === inputValue);
You can use reduce like this: If the searchText matches floor, then return the entire section. Else if the searchText exists in data, then return a section object with filtered data array.
const sectionDummy = [
{ floor: "1", data: ['A', 'B', 'C']},
{ floor: "2", data: ['D', 'E', 'F']},
{ floor: "3", data: ['G', 'H', 'I']},
];
const filter = (sections, searchText) => {
searchText = searchText.trim().toLowerCase();
return sections.reduce((acc, { floor, data }) => {
if (floor === searchText) {
acc.push({ floor, data })
} else {
const filtered = data.filter(d => d.toLowerCase().includes(searchText))
if (filtered.length > 0)
acc.push({ floor, data: filtered })
}
return acc;
}, [])
}
console.log(filter(sectionDummy, "A"))
console.log(filter(sectionDummy, "G"))
console.log(filter(sectionDummy, "1"))
I have an array filled with objects. The following example shows the structure of the objects.
let array = [
{
data: [{name:'a', value:20}, {name:'b', value:10}, {name:'c', value:5}]
},
{
data: [{name:'d', value:20}, {name:'a', value:10}, {name:'e', value:40}]
},
{
data: [{name:'b', value:30}, {name:'a', value:5}]
}
];
I'm trying to iterate through all the data values and summarize all the identical letters and sum up there values in a new array. So the new array should look like this:
let array = [{name:'a', value:35}, {name:'b', value:40}, {name:'c', value:5}, {name:'d', value:20}, {name:'e', value:40}];
This is my current approach but I don't get it to work.
let prevData = '';
let summarizedArray = [];
for(let i = 0; i < array.length; i++) {
for(let j = 0; j < array[i].data.length; j++) {
if(prevData === array[i].data[j].name) {
let summarized = {
name: array[i].data[j].name;
value: prevData.value + array[i].data[j].value;
}
summarizedArray.push(summarized);
}
prevData = array[i].data[j];
}
}
// Edited Example:
let array = [
{
data: [{name:'a', value1:20, value2:90, value3:'foo'},
{name:'b', value1:30, value2:20, value3:'boo'}]
},
data: [{name:'c', value1:5, value2:10, value3:'goo'},
{name:'a', value1:30, value2:20, value3:'foo'}]
},
{
];
The values should be bundled by same names. The values of Value1 and Value2 should be added up and Value3 is always the same for each name.
So the result should look like this:
let result = [{name:'a', value1:50, value2:110, value3:'foo'},
{name:'b', value1:30, value2:20, value3:'boo'},
{name:'c', value1:5, value2:10, value3:'goo'}
];
You could take a Map and collect all values. Later get an array of object of the collected values.
let array = [{ data: [{ name: 'a', value: 20 }, { name: 'b', value: 10 }, { name: 'c', value: 5 }] }, { data: [{ name: 'd', value: 20 }, { name: 'a', value: 10 }, { name: 'd', value: 40 }] }, { data: [{ name: 'b', value: 30 }, { name: 'a', value: 5 }] }],
result = Array.from(
array.reduce(
(m, { data }) => data.reduce(
(n, { name, value }) => n.set(name, (n.get(name) || 0) + value),
m
),
new Map
),
([name, value]) => ({ name, value })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
For a more convoluted object, you could take single properties to add, after a check for the type.
var array = [{ data: [{ name: 'a', value1: 20, value2: 90, value3: 'foo' }, { name: 'b', value1: 30, value2: 20, value3: 'boo' }] }, { data: [{ name: 'c', value1: 5, value2: 10, value3: 'goo' }, { name: 'a', value1: 30, value2: 20, value3: 'foo' }] }],
result = Array.from(
array.reduce(
(m, { data }) => {
data.forEach(o => {
var temp = m.get(o.name);
if (!temp) {
m.set(o.name, temp = {});
}
Object.entries(o).forEach(([k, v]) => {
if (k === 'name') return;
if (typeof v === 'number') {
temp[k] = (temp[k] || 0) + v;
} else {
temp[k] = v;
}
});
});
return m;
},
new Map
),
([name, value]) => Object.assign({ name }, value)
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have an array of objects and I want to sort it by common types.
Some objects have types with 'x', some with 'y' and some with 'z'.
For now, I'm able to sort it and put all the 'x' in front. However, I would want to do the same for 'y', and 'z' too.
In the end, all 'x' would be in front, then all 'y' and then 'z'.
list.sort((a, b) => {
if (a.type !== b.type) {
if (a.type === 'x') { return -1; }
return 1;
}
return a.name.localeCompare(b.name);
});
Any help will be greatly appreciated.
You could use an object for type with the sort order.
var list = [{ type: 'a', name: 'z' }, { type: 'b', name: 'a' }, { type: 'c', name: 'b' }, { type: 'x', name: 'c' }, { type: 'x', name: 'd' }, { type: 'y', name: 'e' }, { type: 'y', name: 'f' }, { type: 'z', name: 'g' }, { type: 'z', name: 'h' }, ]
list.sort(function (a, b) {
var order = { x: -1, y: -1, z: -1, default: 0 };
return (order[a.type] || order.default) - (order[b.type] || order.default) || a.name.localeCompare(b.name);
});
console.log(list);
.as-console-wrapper { max-height: 100% !important; top: 0; }
It works with
{
f: -2, // top
x: -1, // \
y: -1, // a group after top
z: -1, // /
default: 0 // a default value for not mentioned types for sorting in the middle
a: 1 // after the common parts
d: 2 // bottom
}
One simple solution would be to define a numeric order for the types and use the classic approach to sort an array of objects by a numeric property:
var order = {'x': 0, 'y': 1, 'z': 2}
var data = [
{type: 'z'},
{type: 'y'},
{type: 'x'},
{type: 'x'},
{type: 'y'},
{type: 'z'}
]
var sortedData = data.sort(function(a, b) {
return order[a.type] - order[b.type]
})
console.log(sortedData)
Note that you may want to include some error handling, e.g. for types which are not maintained in the order map.