如何过滤由多个相同的属性的对象的阵列

问题描述 投票:0回答:5

How do I filter this array as described in the question

Description

请注意,取值范和entry4分担property: 'subject'property: 'field'相同的值。

Question

进出口寻找一个表演性和清洁的方式来过滤这个数组,并得到共享双方values那些propertyies的条目。

更新:

Returned value

我并不想转换成数据,但分析它。所以从分析中返回的值应该是这样的:

[['entry1', 'entry4'],...]

而这种分析列表,我可以轻松地将我triples = [...]到三倍的名单,我删除条目之一(不事关它,可能是“取值范”或“entry4”),并更新另一个

[
  { subject: "entry1", property: "subject", value: "sport" },
  { subject: "entry1", property: "field", value: "category" },
  { subject: "entry1", property: "content", value: "football" },
  { subject: "entry1", property: "content", value: "basketball" },
]

P.S

  1. 我不是在寻找像一个解决方案: array.filter(({property, value})=> property === 'sport' && value === 'category')

我不知道“运动”或“类别”。这些都是动态值。

  1. 我的实际数据要大得多,并包含更多的属性类型为每个条目。此外它做的好,因为我在这里展示订购。我做了简化,所以请在考虑性能。

代码片段:

const triples = [
  { subject: "entry1", property: "subject", value: "sport" },
  { subject: "entry1", property: "field", value: "category" },
  { subject: "entry1", property: "content", value: "football" },

  { subject: "entry4", property: "subject", value: "sport" },
  { subject: "entry4", property: "field", value: "category" },
  { subject: "entry4", property: "content", value: "basketball" },

  { subject: "entry2", property: "subject", value: "music" },
  { subject: "entry2", property: "field", value: "category" },
  { subject: "entry2", property: "content", value: "notes" },

  { subject: "entry3", property: "subject", value: "painting" },
  { subject: "entry3", property: "field", value: "category" },
  { subject: "entry3", property: "content", value: "drawings" }
];
javascript arrays performance filtering lodash
5个回答
1
投票

我必须说,输入数据结构是不是最佳的,并且使用“主体”既是一个真正的对象属性和数值为property将使它更加混乱。我会打电话给第一个概念(真正的subject)“项”,因为采样值“取值范”,“ENTRY2”,...

这里有一个方法来提取["entry1", "entry4"]为您的样品数据:

  1. 集团通过其进入的对象,其中“财产”和“价值”被翻译成键/值对,所以你会得到这样的数据: { entry1: { subject: "sport", field: "category", content: "football" }, entry4: { subject: "sport", field: "category", content: "basketball" }, entry2: { subject: "music", field: "category", content: "notes" }, entry3: { subject: "painting", field: "category", content: "drawings" } } 这将是更容易使用。下面的代码实际上将创建一个Map,而不是一个普通的对象,但原理是一样的。
  2. 定义这些对象,其中,所述值是由对象和字段,字符串化作为JSON新group属性。例如,上述的结果的第一个对象将与扩展: group: '["sport","category"]'
  3. 创建映射条目,通过它们的群体价值键控。因此,这将导致这种结果: { '["sport","category"]': ["entry1","entry4"], '["music","category"]': ["entry2"], '["painting","category"]': ["entry3"] }
  4. 现在,它是一个简单的步骤,只列出的数值(子阵列),只有那些有多个条目值。

下面是执行:

const triples = [{subject: "entry1", property: "subject", value: "sport"},{subject: "entry1", property: "field", value: "category"},{subject: "entry1", property: "content", value: "football"},{subject: "entry4", property: "subject", value: "sport"},{subject: "entry4", property: "field", value: "category"},{subject: "entry4", property: "content", value: "basketball"},{subject: "entry2", property: "subject", value: "music"},{subject: "entry2", property: "field", value: "category"},{subject: "entry2", property: "content", value: "notes"},{subject: "entry3", property: "subject", value: "painting"},{subject: "entry3", property: "field", value: "category"},{subject: "entry3", property: "content", value: "drawings"},];

// 1. Group the data by subject into objects where "property" and "value" are translated into key/value pairs:
const entries = new Map(triples.map(o => [o.subject, { entry: o.subject }]));
triples.forEach(o => entries.get(o.subject)[o.property] = o.value);
// 2. Define a group value for these objects (composed of subject and field)
entries.forEach(o => o.group = JSON.stringify([o.subject, o.field]));
// 3. Create Map of entries, keyed by their group value
const groups = new Map(Array.from(entries.values(), o => [o.group, []]));
entries.forEach(o => groups.get(o.group).push(o.entry));
// 4. Keep only the subarrays that have more than one value
const result = [...groups.values()].filter(group => group.length > 1);
console.log(result);

请注意,输出是一个嵌套的数组,因为在理论上可能有多种组合的条目,比如[ ["entry1", "entry4"], ["entry123", "entry521", "entry951"] ]

上述可以被修改/扩展以获得最终滤波结果。在第三步,你仍然会收集的对象(不只是入门值),然后将过滤结果被映射回原来的格式:

const triples = [{subject: "entry1", property: "subject", value: "sport"},{subject: "entry1", property: "field", value: "category"},{subject: "entry1", property: "content", value: "football"},{subject: "entry4", property: "subject", value: "sport"},{subject: "entry4", property: "field", value: "category"},{subject: "entry4", property: "content", value: "basketball"},{subject: "entry2", property: "subject", value: "music"},{subject: "entry2", property: "field", value: "category"},{subject: "entry2", property: "content", value: "notes"},{subject: "entry3", property: "subject", value: "painting"},{subject: "entry3", property: "field", value: "category"},{subject: "entry3", property: "content", value: "drawings"},];

// 1. Group the data by subject into objects where "property" and "value" are translated into key/value pairs:
const entries = new Map(triples.map(o => [o.subject, { entry: o.subject }]));
triples.forEach(o => entries.get(o.subject)[o.property] = o.value);
// 2. Define a group value for these objects (composed of subject and field)
entries.forEach(o => o.group = JSON.stringify([o.subject, o.field]));
// 3. Create Map of objects(*), keyed by their group value
const groups = new Map(Array.from(entries.values(), o => [o.group, []]));
entries.forEach(o => groups.get(o.group).push(o));
// 4. Keep only the subarrays that have more than one value
const result = [...groups.values()].filter(group => group.length > 1)
// 5. ...and convert it back to the original format:
    .flatMap(group => [
        { subject: group[0].entry, property: "subject", value: group[0].subject },
        { subject: group[0].entry, property: "field", value: group[0].field },
        ...group.map(o => ({ subject: group[0].entry, property: "content", value: o.content }))
    ]);

console.log(result);

0
投票

可以三元组的阵列减少到一个对象,其中result[propertyString][valueString]与“属性”等于propertyString和“值”等于三元组的valueString的阵列:

triples.reduce((acc, triple) => {
    acc[triple.property] = acc[triple.property] || {};
    acc[triple.property][triple.value] = acc[triple.property][triple.value] || [];
    acc[triple.property][triple.value].push(triple);
    return acc;
}, {})

然后,您可以搜索你想要的属性和值对象,并检查是否有多于一个三。


0
投票

我开始回答这个问题,但我们需要来回走,所以我可以更好地了解你在找什么。

let data = [
  {subject: 'entry1', property: 'subject', value: 'sport'},
	{subject: 'entry1', property: 'field', value: 'category'},
	{subject: 'entry1', property: 'content', value: 'football'},

	{ subject: 'entry4', property: 'subject', value: 'sport' },
  { subject: 'entry4', property: 'field', value: 'category' },
  { subject: 'entry4', property: 'content', value: 'basketball' },

	{subject: 'entry2', property: 'subject', value: 'music'},
	{subject: 'entry2', property: 'field', value: 'category'},
	{subject: 'entry2', property: 'content', value: 'notes'},

	{subject: 'entry3', property: 'subject', value: 'painting'},
	{subject: 'entry3', property: 'field', value: 'category'},
	{subject: 'entry3', property: 'content', value: 'drawing'}
]

let keys = data.map((item, inex) => { return item.subject })

let uniqueKeys = keys.filter((item, index) => { return keys.indexOf(item) >= index })

let propertiesWeCareAbout = ['subject', 'field']

let mappedValues = data.reduce((acc, item, index) => {
    acc[item.subject] = {}
    acc[item.subject].values = data.map((subItm, subIndx) => { if (item.subject === subItm.subject) { if (propertiesWeCareAbout.indexOf(subItm.property) > -1) {return subItm.value} }}).filter(Boolean)
    return acc;
}, {})

// this is where I leave you... because I think you need to finish this up yourself. 
// You have all the mapped data you need to solve your question. 
// You now just need to map over the unique keys checking the `mappedValues` data structure for entries that have the same values in the values array. 
// You can rename things if you want. But these are all the parts of the solution laid out.
// p.s. You can remove the 'category' string from the propertiesWeCareAbout array based on the example you provided... and you can simplify what I've provided in a number of ways.

// this is where you map to get just the strings of "entry1" and "entry4" based on the other mapped data provided. Then you can combine data as you said you need to.
let finalListOfEntriesThatNeedToBeMerged = uniqueKeys.map((item, index) => {return item})

console.log(mappedValues)
console.log(finalListOfEntriesThatNeedToBeMerged)

这就是你要开始。但接下来的步骤取决于您正在寻找的数据映射到什么。

我打算把重点放在未来此评论:“对于这些属性共享这两个值项”


0
投票

使用lodash可以GROUPBY的subject,由新subject属性和field财产转换为对象,GROUPBY对象,并将其转换回项目的数组:

const { flow, partialRight: pr, groupBy, map, set, head, flatMap, toPairs, isArray } = _;

const dontCollect = key => ['entry', 'subject', 'field'].includes(key);
const createPropery = (subject, property, value) => ({ subject, property, value });

const fn = flow(
  pr(groupBy, 'subject'),
  pr(map, (g, entry) => ({ // convert to object with the subject as entry
    entry,
    ...g.reduce((r, o) => set(r, o.property, o.value), {}),
  })),
  pr(groupBy, o => `${o.subject}-${o.field}`),
  pr(map, g => g.length > 1 ? _.mergeWith(...g, (a, b, k) => { // merge everything to an object
    if(dontCollect(k)) return a;
    return [].concat(a, b); // convert non entry, subject, or field properties to array if repeated
  }) : head(g)),
  pr(flatMap, ({ entry: subject, ...o }) => // convert back a series of rows
    flow(
      toPairs,
      pr(flatMap, ([property, value]) => isArray(value) ?
        map(value, v => createPropery(subject, property, v))
        :
        createPropery(subject, property, value)
      )
    )(o)
  )
);

const triples = [{"subject":"entry1","property":"subject","value":"sport"},{"subject":"entry1","property":"field","value":"category"},{"subject":"entry1","property":"content","value":"football"},{"subject":"entry4","property":"subject","value":"sport"},{"subject":"entry4","property":"field","value":"category"},{"subject":"entry4","property":"content","value":"basketball"},{"subject":"entry2","property":"subject","value":"music"},{"subject":"entry2","property":"field","value":"category"},{"subject":"entry2","property":"content","value":"notes"},{"subject":"entry3","property":"subject","value":"painting"},{"subject":"entry3","property":"field","value":"category"},{"subject":"entry3","property":"content","value":"drawings"}];

const result = fn(triples);

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

0
投票

我首先筛选的所有property.subjects并把它们还原成一个多维阵列中,其中每个阵列包含出现较然后一旦受试者值。

然后,我过滤所有property.fields,并检查他们的property.subjectare相等。

然后,我创建一个映射对象(mergeEntriesBysubjectIndex)从哪里获得{0: true, 1: false, 2: true}每个按键对应subjects索引值。

最后,我对mergeEntriesBysubjectIndex运行,每个真正的指数将触发所有三元组基于索引subjects新的合并项,新更新的阵列。

我的实现:

/* 
* @description 
* Get an mulitdimensional array, where each inner array represent a list
* of entries with similar value
* 
* @ return [[], [], []]
*/
const subjects = Object.values(
  triples
    .filter(triple => triple.property === "subject")
    .reduce((subjects, entry) => {
      if (subjects[entry.value]) {
        subjects[entry.value].push(entry.subject);
      } else {
        subjects[entry.value] = [];
        subjects[entry.value].push(entry.subject);
      }
      return subjects;
    }, {})
).filter(arr => arr.length > 1);

const fields = triples.filter(triple => triple.property === "field");

/*
* @description
* Create an object based on the "subjects" mulit-dimensional array from before
* Each key represent the index of "subjects", where the value is a boolean * 
* representing a similar "property:field" value 
*/
const mergeEntriesBysubjectIndex = subjects.reduce((filtered, chunk, index) => {
  let values = [];
  chunk.forEach(subject => {
    const obj = fields.find(field => field.subject === subject).value;
    values.push(obj);
  });
  filtered[index] = values.every((val, i, arr) => val === arr[0]);
  return filtered;
}, {});

/*
* @description
* Get an array of subjects value (e.g. "entry1", "entry2")
* and return a new "merged" collection with uniqe objects
* and with the same name for a subject
*/
const mergeEntries = entries => {
  const ent = triples.filter(triple => triple.subject === entries[0]);
  const newContent = triples
    .filter(
      triple => triple.subject === entries[1] && triple.property === "content"
    )
    .map(triple => ({ ...triple, subject: entries[0] }));
  return [...ent, ...newContent];
};

/*
* @description
* return a new updated list of triples without the specified entries
*/
const removeEntriesFromCurrentTriples = entries =>
  triples.filter(triple => !entries.includes(triple.subject));

for (let index in mergeEntriesBysubjectIndex) {
  if (mergeEntriesBysubjectIndex[index]) {
    const mergeEntry = mergeEntries(subjects[index]);
    const updateEntries = [
      ...removeEntriesFromCurrentTriples(subjects[index]),
      ...mergeEntry
    ];
    // The new trasformed triples collection
    console.log('transformed triples:', updateEntries)
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.