#javascript #node.js #algorithm #data-structures
#javascript #node.js #алгоритм #структуры данных
Вопрос:
Итак, у меня возникли проблемы, пытаясь понять, как правильно отсортировать этот массив хэш-карт. Данные:
let array =
[ { name : 'John' }
, { species : 'Human' }
, { species_sort_order : '1' }
, { name_sort_order : '2' }
, { species : 'Dog' }
, { name : 'oreo' }
, { species_sort_order : '' }
, { name_sort_order : '1' }
, { name_sort_order : '' }
, { name : 'Susan' }
, { species : 'Human' }
, { species_sort_order : '' }
// , ... and so on
]
1) Итак, существует 4 пары ключ-значение:
- Имя,
- Виды,
- name_sort_order,
- виды_sort_order
2) Связанные значения будут отображаться рядом друг с другом; однако они могут располагаться в случайном порядке, как показано выше и ниже:
{name: "John"}, {species: "Human"}, {species_sort_order: "1"}, {name_sort_order: "2"}
{species_sort_order: "1"}, {species: "Human"}, {name_sort_order: "2"}, {name: "John"}
{species: "Human"}, {species_sort_order: "1"}, {name: "John"}, {name_sort_order: "2"}
3) Если значение sort_order является пустой строкой («»), то у него нет позиции сортировки, и его размещение не имеет значения.
4) Желаемый результат будет выглядеть следующим образом
<h2>Human</h2>
<ul>
<li>John</li>
<li>Susan</li>
</ul>
<h2>Dog</h2>
<ul>
<li>Oreo</li>
</ul>
Это уже многословный пост; поэтому я не буду перечислять все свои попытки, но также я не ищу кого-то, кто дал бы мне код для этого (хотя вы можете, если хотите), я фанат проблем со структурой данных и алгоритмами… но это не какая-то забавная проблема с кодированием, она имеет реальные последствия, поэтому я просто смотрю, есть ли у кого-нибудь хорошие стратегии.
До сих пор мой мыслительный процесс:
1)
// Iterate through the list of hashmaps,
// if the index is dividable by 3 (i%3),
// then push empty array
// push hashmap to the last array in the array (e.g. ar[ar.length-1].push(hashmap))
// otherwise
// push hashmap to the last array in the array (e.g. ar[ar.length-1].push(hashmap))
- Теперь у меня есть массив массивов, где каждый массив содержит связанные значения хэш-карты.
Итак, мой следующий мыслительный процесс — начать сортировку массива в порядке
- A) species_sort_order затем
- B) name_sort_order… но это стало очень дорогостоящим алгоритмом с точки зрения временной сложности и просто не кажется эффективным.
РЕДАКТИРОВАТЬ: я знаю, что исходный ввод (массив объектов) усложняет задачу больше, чем нужно; но это данные, возвращаемые из ответа AJAX.
Я не могу использовать какие-либо внешние библиотеки, только JavaScript и его встроенные функции
Комментарии:
1. Этот вопрос заставляет меня задуматься, почему ваш массив объектов такой, какой он есть. Он помещает свойства, принадлежащие одному и тому же объекту, в отдельные объекты, что значительно усложняет сохранение данных при сортировке. Я бы сказал, сначала исправьте все, что генерирует эти данные. Тогда сортировка будет тривиальной.
2. может
species_sort_order
иname_sort_order
иметь буквенное значение и не может быть изменено на целочисленные значения (или значения с плавающей запятой?)?3. @jfriend00 Я знаю, что исходный ввод (массив объектов) усложняет задачу больше, чем нужно; но это данные, возвращаемые из ответа AJAX.
4. @MisterJojo нет, у них могут быть только целые числа в строковом формате или пустая строка. Вы определенно можете выполнить parseInt(), чтобы преобразовать «1» в 1
5. Логика порядка ключей неясна. Глядя на ваш вывод HTML, Human-John` имеет значения ключей
1-2
,Dog-oreo
->empty-2
иHuman-Susan
->empty-empty
. КакDog
и раньшеSusan
, и оба они имеют и имеют ключ для species_sort_order . Ваше объяснение указывает, что их взаимный порядок остается неизменным, что нарушает группировку видов. Если мы сортируем сначала поspecies
, а затем поspecies_sort_order
, группировка сохраняется, но в этом случаеspecies:'Dog'
перед «species:»Human», чего больше нет в вашем выводе html.
Ответ №1:
Я думаю, что Lodash может быть хорошим способом добиться этого
const arr = [ {name: "John"}, {species: "Human"}, {species_sort_order: "1"}, {name_sort_order: "2"},
{species: "Dog"}, {name: "oreo"}, {species_sort_order: ""}, {name_sort_order: "1"},
{name_sort_order: ""}, {name: "Susan"}, {species: "Human"}, {species_sort_order: ""}]
_.orderBy(arr, ['name', 'species'], ['asc', 'asc'])
Комментарии:
1. К сожалению, я должен был заметить, что я не могу использовать какие-либо внешние библиотеки, только JavaScript и его встроенные функции
2. Звучит подозрительно, как проблема с домашним заданием
3. @Hive7 lol Я специализировался в области компьютерных наук, и я посещал полный учебный лагерь stack … поверьте мне, ни одна школа не задает такие сложные задачи! Это реальный мир, он приходит в ответ на запрос AJAX.
4. Похоже, это не относится к требуемой группировке.
Ответ №2:
Делая потенциально неверное предположение о реальных требованиях к сортировке, которые все еще кажутся нечеткими в свете потенциальных пустых строковых значений, я могу придумать это:
// utility functions
const chunk = (n) => (xs) =>
xs .length <= n ? [[...xs]] : [xs .slice (0, n), ... chunk (n) (xs .slice (n))]
const group = (fn) => (xs) =>
Object .values (xs .reduce (
(a, x) => ({...a, [fn(x)]: [...(a[fn(x)] || []), x]}),
{}
))
// helper function
const makeRecords = (xs) =>
chunk (4) (xs) .map (gs => Object .assign (...gs))
// main function
const restructure = (array) => group ((x) => x.species) (makeRecords (array))
.sort (([{species_sort_order: s1}], [{species_sort_order: s2}]) => (s1 || Infinity) - (s2 || Infinity))
.map (s => s.sort (({name_sort_order: n1}, {name_sort_order: n2}) => (n1 || Infinity) - (n2 || Infinity)))
// sample data
const array = [{name: "John"}, {species: "Human"}, {species_sort_order: "1"}, {name_sort_order: "2"}, {species: "Dog"}, {name: "oreo"}, {species_sort_order: ""}, {name_sort_order: "1"}, {name_sort_order: ""}, {name: "Susan"}, {species: "Human"}, {species_sort_order: ""}]
// demo
const results = restructure (array)
document .getElementById ('output') .innerHTML = results .map (xs => `<h2>${xs[0].species}</h2><ul>${xs.map(x => `<li>${x.name}</li>`).join('')}</ul>`).join('')
document .getElementById ('result') .innerHTML = `<pre>${JSON.stringify(results, null, 4)}</pre>`
<div id="output"></div><hr/><h3>Transformed data</h1><pre id="result"></pre>
Начнем с двух служебных функций:
chunk
разбивает массив наn
подмассивы длиной -long (при этом последний, возможно, короче).chunk (3) ([8, 6, 7, 5, 3, 0, 9]) //=> [[8, 6, 7], [5, 3, 0], [9]]`
group
принимает функцию, возвращающую строку / число, и массив значений, группируя значения в подмассивы с тем же результатом при применении функции.group (n => n % 10) ([23, 12, 16, 33, 3, 42]) //=> [[12, 42], [23, 33, 3], [16]] ``
Тогда у нас есть единственная вспомогательная функция:
makeRecords
берет массив ваших входных элементов, разбивает их на группы по четыре и объединяет результат в элементы, которые выглядят следующим образом:{ name: "John", species: "Human", species_sort_order: "1", name_sort_order: "2" }
Затем наша основная функция:
restructure
принимает ваш ввод, превращает его в записи сmakeRecords
помощью, вызываетgroup
группировку их по видам, затем сортирует виды в соответствии с первымspecies_sort_order
в каждой группе и сортирует группы внутри по ихname_sort_order
s.
Предполагается, что элементы без ключей сортировки могут быть просто размещены в конце их группы в произвольном порядке. Во-вторых, мы можем использовать первую запись в каждой группе, чтобы выбрать способ сортировки групп. Это то, что я считал необоснованным. Но если есть другое требование, его, вероятно, следует изложить более подробно, с более полным примером.
В комментарии было высказано предположение, что эта проблема слишком сложна для школьного задания. Это не так. Я получал и давал гораздо более сложные задания. Если разбить это на небольшие части, которые хорошо выполняют одну работу, довольно легко разбить это на шаги.
В этом ключе я мог бы предложить настройку group
, используя две дополнительные функции, push
которые помещают значение в массив и возвращают массив, а set
также устанавливают значение свойства для объекта. Как правило, я предпочитаю избегать изменения данных, но есть исключение для данных, внутренних для функции, и здесь это может избежать потенциальной проблемы с производительностью. Поэтому вместо этого мы могли бы написать это так:
const push = (x) => (xs = []) =>
((xs .push (x)), xs)
const set = (v) => (k) => (o = {}) =>
((o[k] = v), o)
const group = (fn) => (xs) =>
Object .values (xs .reduce (
(a, x) => set (push (x) (a [fn (x)])) (fn(x)) (a),
{}
))
Ответ №3:
насколько это возможно из того, что я могу сделать…
const keyS = (str,lg) => ((str||'') ' '.repeat(lg)).substring(0, lg)
const keyN = (num,def,mask) => (!num) ? ((!def) ? '9'.repeat(mask.length) : `${mask}${def}`.slice(-mask.length)) : `${mask}${num}`.slice(-mask.length)
const data =
[ { name : 'John' }
, { species : 'Human' }
, { species_sort_order : '1' }
, { name_sort_order : '2' }
, { species : 'Dog' }
, { name : 'oreo' }
, { species_sort_order : '' }
, { name_sort_order : '1' }
, { name_sort_order : '' }
, { name : 'Susan' }
, { species : 'Human' }
, { species_sort_order : '' }
// , ... and so on
]
, dataOrd = data.reduce((a,c,i,{[i 1]:next})=>
{
let ref = i%4, pos = Math.floor(i/4);
if (!ref ) a.r.push({...c})
else a.r[pos] = {...a.r[pos],...c} // group 4 by 4 objects
if (ref===3) // === last item of 4
{
let grp = a.r[pos]
a.nso = Math.max( a.nso, (grp.name_sort_order || 0)) // get max
a.sso = Math.max( a.sso, (grp.species_sort_order || 0)) // values amp;
a.sLg = Math.max( a.sLg, (grp.species || '').length) // max length
if (grp.species_sort_order != '') // memorize sort orders
a.sKeys[grp.species] = grp.species_sort_order; // by species names
}
if (!!next) return a // still in reduce loop
else // no next element -> reduce loop final
{
let nsoMask = '0'.repeat( ( a.nso).toString(10).length) // keys
, ssoMask = '0'.repeat( ( a.sso).toString(10).length) // mask buiders
;
a.r.forEach(({species, species_sort_order, name, name_sort_order},i,t) =>
{
t[i].key = keyN( species_sort_order, a.sKeys[species], ssoMask )
keyS( species, a.sLg )
keyN( name_sort_order, '', nsoMask )
})
a.r.sort((o1, o2) => o1.key.localeCompare(o2.key))
// console.log(JSON.stringify(a,0,2))
return a.r
}
},{ r:[], nso:0, sso:0, sLg:0, sKeys:{} })
// displaying HTML result
const divOutPut = document.querySelector('div#outPut')
let speciesGrp = ''
, speciesElm = null
, ulGrp = null
, liElm = null
dataOrd.forEach(({species, name}) =>
{
if (speciesGrp != species)
{
speciesElm = divOutPut.appendChild(document.createElement('h2'))
ulGrp = divOutPut.appendChild(document.createElement('ul'))
speciesElm.textContent = speciesGrp = species
}
liElm = ulGrp.appendChild(document.createElement('li'))
liElm.textContent = name
})
<div id="outPut"></div>
Комментарии:
1. @ezg Я потратил время на разработку этого ответа, мне бы хотелось иметь хотя бы один комментарий…
2. @ezg это не только место для вопросов и ответов, но и место для обмена информацией, где также применяется сетевой этикет en.wikipedia.org/wiki/Etiquette_in_technology#Netiquette